当前位置:   article > 正文

Android-9: 持续打印non-protected broadcast 问题_sending non-protected broadcast android.intent.act

sending non-protected broadcast android.intent.action.private_time_tick from

1. 问题

将App打包成系统App时,即使在自己App的AndroidManifest.xml中声明了的新增protected-broadcast广播,为什么发送广播时还总是打印如下log:
ActivityManager: Sending non-protected broadcast

2. 分析

Log是在ActivityManagerService.java的以下代码打印出来的。
从以下代码看,会检测是不是isProtectedBroadcast广播,如果是就直接返回,则不会再打印此警告了。

   private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,
        String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) {
    if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
        // Don't yell about broadcasts sent via shell
        return;
    }

    final String action = intent.getAction();
    if (isProtectedBroadcast
            || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
            || Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action)
            || Intent.ACTION_MEDIA_BUTTON.equals(action)
            || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
            || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
            || Intent.ACTION_MASTER_CLEAR.equals(action)
            || Intent.ACTION_FACTORY_RESET.equals(action)
            || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
            || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
            || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
            || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
            || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)
            || AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action)
            || AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION.equals(action)) {
        // Broadcast is either protected, or it's a public action that
        // we've relaxed, so it's fine for system internals to send.
        return;
    }

    // This broadcast may be a problem...  but there are often system components that
    // want to send an internal broadcast to themselves, which is annoying to have to
    // explicitly list each action as a protected broadcast, so we will check for that
    // one safe case and allow it: an explicit broadcast, only being received by something
    // that has protected itself.
    if (intent.getPackage() != null || intent.getComponent() != null) {
        if (receivers == null || receivers.size() == 0) {
            // Intent is explicit and there's no receivers.
            // This happens, e.g. , when a system component sends a broadcast to
            // its own runtime receiver, and there's no manifest receivers for it,
            // because this method is called twice for each broadcast,
            // for runtime receivers and manifest receivers and the later check would find
            // no receivers.
            return;
        }
        boolean allProtected = true;
        for (int i = receivers.size()-1; i >= 0; i--) {
            Object target = receivers.get(i);
            if (target instanceof ResolveInfo) {
                ResolveInfo ri = (ResolveInfo)target;
                if (ri.activityInfo.exported && ri.activityInfo.permission == null) {
                    allProtected = false;
                    break;
                }
            } else {
                BroadcastFilter bf = (BroadcastFilter)target;
                if (bf.requiredPermission == null) {
                    allProtected = false;
                    break;
                }
            }
        }
        if (allProtected) {
            // All safe!
            return;
        }
    }

    // The vast majority of broadcasts sent from system internals
    // should be protected to avoid security holes, so yell loudly
    // to ensure we examine these cases.
    if (callerApp != null) {
        Log.wtf(TAG, "Sending non-protected broadcast " + action
                        + " from system " + callerApp.toShortString() + " pkg " + callerPackage,
                new Throwable());
    } else {
        Log.wtf(TAG, "Sending non-protected broadcast " + action
                        + " from system uid " + UserHandle.formatUid(callingUid)
                        + " pkg " + callerPackage,
                new Throwable());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

而isProtectedBroadcast是通过以下代码获取到当前广播是否是protected的广播
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);

isProtectedBroadcast()函数的具体实现是在PackageManagerService.java中如下代码:

5543    @Override
5544    public boolean isProtectedBroadcast(String actionName) {
5545        // allow instant applications
5546        synchronized (mProtectedBroadcasts) {
5547            if (mProtectedBroadcasts.contains(actionName)) {
5548                return true;
5549            } else if (actionName != null) {
5550                // TODO: remove these terrible hacks
5551                if (actionName.startsWith("android.net.netmon.lingerExpired")
5552                        || actionName.startsWith("com.android.server.sip.SipWakeupTimer")
5553                        || actionName.startsWith("com.android.internal.telephony.data-reconnect")
5554                        || actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) {
5555                    return true;
5556                }
5557            }
5558        }
5559        return false;
5560    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

通过以上代码可知,实际是PackageManagerService里面维护一个名为mProtectedBroadcasts的系统广播白名单。在PackageManagerService扫描系统App时会将AndroidManifest.xml中的所有protected-broadcast加入到此ArraySet变量。

11298    private void commitPackageSettings(PackageParser.Package pkg,
11299            @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user,
11300            final @ScanFlags int scanFlags, boolean chatty) {
					.........
11634            if (pkg.protectedBroadcasts != null) {
11635                N = pkg.protectedBroadcasts.size();
11636                synchronized (mProtectedBroadcasts) {
11637                    for (i = 0; i < N; i++) {
11638                        mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
11639                    }
11640                }
11641            }					
				.......
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

PackageManagerService每次启动时会扫描下面三个目录的App:
/system/app
/system/priv-app
/system/framework/framework-res.apk

所以理论上只要在自己App中的AndroidManifest.xml中将自己新增的广播声明为protected-broadcast即可,不应该会出现Sending non-protected broadcast才对。

无意中看到加入广播白名单的参数有一个protectedBroadcasts,通过搜寻发现PackageManagerService中还有如下代码段:

10808    private static void applyPolicy(PackageParser.Package pkg, final @ParseFlags int parseFlags,
10809            final @ScanFlags int scanFlags, PackageParser.Package platformPkg) {
					  .........
10847        if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
10848            // clear protected broadcasts
10849            pkg.protectedBroadcasts = null;
10850            // ignore export request for single user receivers
10851            if (pkg.receivers != null) {
10852                for (int i = pkg.receivers.size() - 1; i >= 0; --i) {
10853                    final PackageParser.Activity receiver = pkg.receivers.get(i);
10854                    if ((receiver.info.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) {
10855                        receiver.info.exported = false;
10856                    }
10857                }
10858            }
					...........
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

从以上代码可知,PackageManagerService在扫描App时,发现如果App不是来自priv-app目录下的App(当然framework-res.apk除外),就会将此App的中声明的protected广播清空,这样这个广播就不会加入到白名单,所以才就一直打印上面的warning log

不过这个仅仅是warning的log而已,广播还是可以正常发送和接收的。只是如果持续打印,会影响到系统性能的,而且看log也心塞,持续打印没用的log。

3. 解决办法

有两个解决办法:

  1. 简单粗暴,将自定义的广播在frameworks/base/core/res/AndroidManifest.xml中声明为protected-broadcast
	29    <protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
	30    <protected-broadcast android:name="android.intent.action.SCREEN_ON" />
	31    <protected-broadcast android:name="android.intent.action.USER_PRESENT" />
	32    <protected-broadcast android:name="android.intent.action.TIME_SET" />
	33    <protected-broadcast android:name="android.intent.action.TIME_TICK" />
	34    <protected-broadcast android:name="android.intent.action.TIMEZONE_CHANGED" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. 如果不希望改Android的源码,可以将自己的App放置到/system/priv-app下,而不是默认的/system/app目录下:
    可在自己app的Android.mk中指定路径:
    LOCAL_MODULE_PATH := $(TARGET_OUT_APPS_PRIVILEGED)

    但是特别注意的是:如果App放置到priv-app目录,是会一些权限限制的。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/443435
推荐阅读
相关标签
  

闽ICP备14008679号