Android 8.0調査メモ：ブロードキャストがもう自由に使えない件


## これは何？
https://developer.android.com/about/versions/oreo/background.html#broadcasts
Android 8.0からは、バックグラウンド動作制限の一環として、AndroidManifestに定義したブロードキャストレシーバーは[一部を除き](https://developer.android.com/guide/components/broadcast-exceptions.html)動作しなくなるよ、という残念なお知らせ。

残念なんだけど、どうやってそんな残念な仕組みが実現されてるのかは知っておきたい。
ということで調査した自分用メモです。

## ソースを読むぞ

「一部を除き」をヒントにAndroidフレームワークの実装部を見てみる。

### どうやって一部を除くことができるようになっているのか？

なんとなくACTION_EVENT_REMINDERを飛ばしてるところを検索してみると

http://tools.oesf.biz/android-8.0.0_r1.0/xref/frameworks/base/core/java/android/provider/CalendarContract.java#2395

```java
   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が付いているとどういう特殊なことになっているのか

http://tools.oesf.biz/android-8.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java#1271

```java
    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のパーサー側は本当に変わってないのかな？

http://tools.oesf.biz/android-8.0.0_r1.0/xref/frameworks/base/core/java/android/content/pm/PackageParser.java

```java
   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);
```

ただパースしてるだけだな。
きっと変わってない。
　
　
## ということは...

```java
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で捨てられるよ、ということみたい。