LoginSignup
18
10

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-11-28

これは何?

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

18
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
10