从Android O(Android 8.0)开始,Android限制了隐式广播的接收(https://developer.android.com/about/versions/oreo/background
),在开发过程中遇到“Background execution not allowed: receiving”log输出,梳理具体情况如下
哪些情况受到影响?
1.隐式发送的广播 ,一般为AndroidManifest.xml中静态注册的Receiver
这种类型一般是隐式的,即发送方没有指明接收者
(注意但如果是上述静态注册的Receiver但发送者指明了接收者,这类广播还是能收到的)
哪些情况不受影响?
1.发送方:显式广播 ,即发送方发送广播的时候指明接收者
通过Intent setPackage或Intent setComponent
2.接收方:Receiver接收者动态注册的方式
通过registerReceiver的方式
3.发送方:发送广播的时候添加了FLAG_RECEIVER_INCLUDE_BACKGROUND的flag
4.接收方:发送的广播需要接收者有权限校验,并且这个权限是系统定义并且安全级别为仅签名 (signature)的
这个有点坑,查看google的提交记录,他们早期考虑是第三方也适用的,只要这个广播有权限校验便不受限制,后来
最后的版本添加了只有系统定义的权限才适用
/**
* Return true if all given permissions are signature-only perms.
*/
final boolean isSignaturePerm(String[] perms) {
if (perms == null) {
return false;
}
IPackageManager pm = AppGlobals.getPackageManager();
for (int i = perms.length-1; i >= 0; i--) {
try {
PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0);
if (pi == null) {
// a required permission that no package has actually
// defined cannot be signature-required.
return false;
}
if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE
| PermissionInfo.PROTECTION_FLAG_PRIVILEGED))
!= PermissionInfo.PROTECTION_SIGNATURE) {
// If this a signature permission and NOT allowed for privileged apps, it
// is okay... otherwise, nope!
return false;
}
} catch (RemoteException e) {
return false;
}
}
return true;
}
5.系统发送的部分广播 :系统白名单的广播不受影响
系统发送这些广播的时候带了 FLAG_RECEIVER_INCLUDE_BACKGROUND标志的广播:
https://developer.android.google.cn/guide/components/broadcast-exceptions
配置白名单:
源码目录/frameworks/base/data/etc/framework-sysconfig.xml
if (action != null) {
if (getBackgroundLaunchBroadcasts().contains(action)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Broadcast action " + action + " forcing include-background");
}
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
}
6.发送方:PendingIntent.getBroadcast
通过查看系统源码逻辑(安全方面考虑),这种方式是显式发送
@UnsupportedAppUsage
public static PendingIntent getBroadcastAsUser(Context context, int requestCode,
Intent intent, int flags, UserHandle userHandle) {
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSender(
ActivityManager.INTENT_SENDER_BROADCAST, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, null, userHandle.getIdentifier());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
7.APP:APP升级了targetsdk>=26,但是跑在Android 0以下的机器,自然没有这个特性
解决方法
1.接收方:动态注册
2.接收方:使用JobScheduler(网络状态等条件可替代部分广播功能,注意国内os对后台特性的修改)
2.发送方:发送广播时增加FLAG_RECEIVER_INCLUDE_BACKGROUND标志
3.接收方:添加权限校验(对第三方应用不适用)
4.常驻后台应用中转(对第三方应用不适用)
技术细节与原理
限制隐式广播的关键代码逻辑如下 (BroadcastQueue.java)
} else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
|| (r.intent.getComponent() == null
&& r.intent.getPackage() == null
&& ((r.intent.getFlags()
& Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
&& !isSignaturePerm(r.requiredPermissions))) {
mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
component.getPackageName());
Slog.w(TAG, "Background execution not allowed: receiving "
+ r.intent + " to "
+ component.flattenToShortString());
skip = true;
}
}
}
为什么动态注册的广播不受限制?
通过查看系统源码BroadcastQueue的逻辑得知