-
产生原因
查看源码在ActivityManagerService中有个checkBroadcastFromSystem方法
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)
|| TelephonyManager.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());
}
}
-
翻译后如下:
-
解决办法
如果该广播是an explicit broadcast,且该receiver的android:exported为false,或者ri.activityInfo.permission!=null,及该receiver加了权限保护,系统则认为这个广播时做过保护了的,便不会打这个log,是个规范使用的广播。
一.本地广播
系统应用可以使用本地广播进行操作是可以满足检查安全的需求。
二.静态广播(两个步骤):
a.如果是系统级独立应用的广播
可以在应用的AndroidManifest.xml里声明为保护广播就可以了。不过注意验证的时候,需要使用adb push到system/app或者system/priv-app/下再重启安卓(AndroidManifest修改需要重启)验证;使用adb install -r后验证依然会报未保护提醒。
<protected-broadcast android:name="com.android.***" />
b. 使用指定包名并且加权限保护
声明.:在Androidmanifest.xml里声明receiver的时候加上自定义的权限,如果是仅需应用内接收,可以将android:exported属性设置为false;
<receiver android:name=".DemoReceiver"
android:exported="false"
android:permission="com.android.permission.RECV.XXX">
<intent-filter android:priority="1000">
<action android:name="com.android.demo.test.XXX"/>
</intent-filter>
</receiver>
使用: 发送广播的地方指定包名或者组件名
Intent i = new Intent("com.android.demo.test.XXX");
i.setPackage("com.***.broadcasttest");
sendBroadcast(i,"com.android.permission.RECV.XXX");
注意指给广播加权限是不够的,在checkBroadcastFromSystem对未在framework中声明为保护广播的系统应用自定义广播进行安全检查的前提是这是一个explicit的广播。因此满足上面两步后才能真正消除警告的warn log.
验证:这样声明后,使用adb发送本广播,如果没有root权限回出现如下错误。有效进行了权限保护。
zhang@Zhang-PC ~ % adb -s 141834343253480E shell am broadcast -a com.***.next
Broadcasting: Intent { act=com.***.next flg=0x400000 }
Exception occurred while executing 'broadcast':
java.lang.SecurityException: Permission Denial: not allowed to send broadcast com.***.next from pid=10387, uid=2000
at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:12979)
at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:12825)
at com.android.server.am.ActivityManagerService.broadcastIntentWithFeature(ActivityManagerService.java:13660)
at com.android.server.am.ActivityManagerShellCommand.runSendBroadcast(ActivityManagerShellCommand.java:765)
at com.android.server.am.ActivityManagerShellCommand.onCommand(ActivityManagerShellCommand.java:206)
at com.android.modules.utils.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:97)
at android.os.ShellCommand.exec(ShellCommand.java:38)
at com.android.server.am.ActivityManagerService.onShellCommand(ActivityManagerService.java:8679)
at android.os.Binder.shellCommand(Binder.java:950)
at android.os.Binder.onTransact(Binder.java:834)
at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:5106)
at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2497)
at android.os.Binder.execTransactInternal(Binder.java:1184)
at android.os.Binder.execTransact(Binder.java:1143)
c. 如果是非独立应用的系统组件,或者独立系统应用使用的公共的广播
推荐在framework/base/core/res/AndroidManifest.xml中有声明为保护广播。
-
扩展了解
广播权限
第一种场景: 谁有权收我的广播?
在这种情况下,可以在自己应用发广播时添加参数声明Receiver所需的权限。
首先,在Androidmanifest.xml中定义新的权限RECV_XXX,例如:
<permission android:name = "com.android.permission.RECV_XXX"/>
然后,在Sender app发送广播时将此权限作为参数传入,如下:
sendBroadcast("com.android.XXX_ACTION", "com.android.permission.RECV_XXX");
这样做之后就使得只有具有RECV_XXX权限的Receiver才能接收此广播要接收该广播,在Receiver应用的AndroidManifest.xml中要添加对应的RECV_XXX权限。
<uses-permission android:name="com.android.permission.RECV_XXX"></uses-permission>
第二种场景: 谁有权给我发广播?
在这种情况下,需要在Receiver app的<receiver> tag中声明一下Sender app应该具有的权限。
首先同上,在AndroidManifest.xml中定义新的权限SEND_XXX,例如:
<permission android:name="com.android.SEND_XXX"/>
然后,在Receiver app的Androidmanifest.xml中的<receiver>tag里添加权限SEND_XXX的声明,如下:
<receiver android:name=".XXXReceiver" android:permission="com.android.permission.SEND_XXX">
<intent-filter>
<action android:name="com.android.XXX_ACTION" />
</intent-filter>
</receiver>
这样一来,该Receiver便只能接收来自具有该SEND_XXX权限的应用发出的广播。
要发送这种广播,需要在Sender app的AndroidManifest.xml中也声明使用该权限即可,如下:
<uses-permission android:name="com.android.permission.SEND_XXX"></uses-permission>
本地广播LocalBroadcastManager
对于LocalBroadcastManager在google官方文档中也说得很清楚,比较简短,也很好看懂,可以去看看。
Helper to register for and send broadcasts of Intents to local objects within your process. This has a number of advantages over sending global broadcasts with sendBroadcast(Intent):You know that the data you are broadcasting won’t leave your app, so don’t need to worry about leaking private data. It is not possible for other applications to send these broadcasts to your app, so you don’t need to worry about having security holes they can exploit. It is more efficient than sending a global broadcast through the system.
大体介绍就是这些,顾名思义,本地广播(注册),数据安全,其他app也不能给你发广播(接收)。也比系统广播高效。
一般使用在应用内部不同fragment和Activity的交互,或者界面和service 的交互。BroadcastReceiver设计的初衷是从全局考虑可以方便应用程序和系统、应用程序之间、应用程序内的通信,所以对单个应用程序而言BroadcastReceiver是存在安全性问题的(恶意程序脚本不断的去发送你所接收的广播)。为了解决这个问题LocalBroadcastManager就应运而生了。
发送
LocalBroadcastManager lcm=LocalBroadcastManager.getInstance(mContext);
lcm.sendBroadcast(new Intent(ACTION_LOCATION));
接收
LocalBroadcastManager mLocalBroadcastManager=LocalBroadcastManager.getInstance(this);
mBoradCast = new MyBroadCast(); //定义广播,广播里面接收处理具体事务
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_LOCATION);
//本地注册,本地接收。
mLocalBroadcastManager.registerReceiver(mBoradCast, intentFilter);
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) { //处理具体事务
String action = intent.getAction();
Log.d(TAG, "onReceive: action = " + action);
}
};
参考:
Android Broadcast 和 BroadcastReceiver的权限限制_android 10 broadcastreceiver 发起限制-CSDN博客