赞
踩
将App打包成系统App时,即使在自己App的AndroidManifest.xml中声明了的新增protected-broadcast广播,为什么发送广播时还总是打印如下log:
ActivityManager: Sending non-protected broadcast
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()); } }
而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 }
通过以上代码可知,实际是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 }
.......
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 } ...........
从以上代码可知,PackageManagerService在扫描App时,发现如果App不是来自priv-app目录下的App(当然framework-res.apk除外),就会将此App的中声明的protected广播清空,这样这个广播就不会加入到白名单,所以才就一直打印上面的warning log
不过这个仅仅是warning的log而已,广播还是可以正常发送和接收的。只是如果持续打印,会影响到系统性能的,而且看log也心塞,持续打印没用的log。
有两个解决办法:
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" />
如果不希望改Android的源码,可以将自己的App放置到/system/priv-app下,而不是默认的/system/app目录下:
可在自己app的Android.mk中指定路径:
LOCAL_MODULE_PATH := $(TARGET_OUT_APPS_PRIVILEGED)
但是特别注意的是:如果App放置到priv-app目录,是会一些权限限制的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。