これは何?
https://developer.android.com/about/versions/oreo/background.html#broadcasts
Android 8.0からは、バックグラウンド動作制限の一環として、AndroidManifestに定義したブロードキャストレシーバーは一部を除き動作しなくなるよ、という残念なお知らせ。
残念なんだけど、どうやってそんな残念な仕組みが実現されてるのかは知っておきたい。
ということで調査した自分用メモです。
ソースを読むぞ
「一部を除き」をヒントにAndroidフレームワークの実装部を見てみる。
どうやって一部を除くことができるようになっているのか?
なんとなくACTION_EVENT_REMINDERを飛ばしてるところを検索してみると
2392 Intent intent = new Intent(ACTION_EVENT_REMINDER);
2393 intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
2394 intent.putExtra(ALARM_TIME, alarmTime);
2395 intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
とかいうのをセットしてる。これだ!
FLAG_RECEIVER_INCLUDE_BACKGROUNDが付いているとどういう特殊なことになっているのか
820 final void processNextBroadcast(boolean fromMsg) {
821 synchronized(mService) {
822 BroadcastRecord r;
...
1254 final int allowed = mService.getAppStartModeLocked(
1255 info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
1256 info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false);
1257 if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
...
1267 } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
1268 || (r.intent.getComponent() == null
1269 && r.intent.getPackage() == null
1270 && ((r.intent.getFlags()
1271 & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
1272 && !isSignaturePerm(r.requiredPermissions))) {
1273 mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
1274 component.getPackageName());
1275 Slog.w(TAG, "Background execution not allowed: receiving "
1276 + r.intent + " to "
1277 + component.flattenToShortString());
1278 skip = true;
ここだけ読むとわかるようなわからんような感じだけど、おそらく、
AndroidManifestに書いてあるreceiverの内容はフレームワークにはちゃんと読まれているけど、
キューに積まれていても無視しますよー(skip = true
する)ってことなのかな?
r.intent.getComponent() == null && r.intent.getPackage() == null
っていうのが、暗黙的(=コンポーネント指定のない)ブロードキャストが対称になってるということだ。
あと、ここで FLAG_RECEIVER_EXCLUDE_BACKGROUND
っていうのがいきなり出てきたぞ?
でも誰もセットしてないからまぁいいか。
http://tools.oesf.biz/android-8.0.0_r1.0/search?q=FLAG_RECEIVER_EXCLUDE_BACKGROUND
AndroidManifestのパーサー側は本当に変わってないのかな?
3333 /**
3334 * Parse the {@code application} XML tree at the current parse location in a
3335 * <em>base APK</em> manifest.
3336 * <p>
3337 * When adding new features, carefully consider if they should also be
3338 * supported by split APKs.
3339 */
3340 private boolean parseBaseApplication(Package owner, Resources res,
3341 XmlResourceParser parser, int flags, String[] outError)
3342 throws XmlPullParserException, IOException {
3343 final ApplicationInfo ai = owner.applicationInfo;
3344 final String pkgName = owner.applicationInfo.packageName;
3345
3346 TypedArray sa = res.obtainAttributes(parser,
3347 com.android.internal.R.styleable.AndroidManifestApplication);
3348
...
3659 } else if (tagName.equals("receiver")) {
3660 Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
3661 if (a == null) {
3662 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
3663 return false;
3664 }
3665
3666 owner.receivers.add(a);
ただパースしてるだけだな。
きっと変わってない。
ということは...
Intent intent = new Intent(MY_ACTION);
intent.addFlags(0x01000000); // ←Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
context.sendBroadcast(intent);
みたいなのをやったらAndroidManifestに登録してるレシーバーにも届くんじゃないのかな?
わからんけど。
まとめ
AndroidManifestに書いてあるものは、Androidフレームワークにレシーバーとして認識はされるけど、いざそのブロードキャストが飛んできたときに、FLAG_RECEIVER_INCLUDE_BACKGROUND指定のない暗黙的インテントはBroadcastQueueで捨てられるよ、ということみたい。