從Android O(Android 8.0)開始税娜,Android限制了隱式廣播的接收(https://developer.android.com/about/versions/oreo/background
),在開發(fā)過程中遇到“Background execution not allowed: receiving”log輸出况脆,梳理具體情況如下
哪些情況受到影響?
1.隱式發(fā)送的廣播 撩笆,一般為AndroidManifest.xml中靜態(tài)注冊的Receiver
這種類型一般是隱式的乃沙,即發(fā)送方?jīng)]有指明接收者
(注意但如果是上述靜態(tài)注冊的Receiver但發(fā)送者指明了接收者猎塞,這類廣播還是能收到的)
哪些情況不受影響?
1.發(fā)送方:顯式廣播 蔓罚,即發(fā)送方發(fā)送廣播的時候指明接收者
通過Intent setPackage或Intent setComponent
2.接收方:Receiver接收者動態(tài)注冊的方式
通過registerReceiver的方式
3.發(fā)送方:發(fā)送廣播的時候添加了FLAG_RECEIVER_INCLUDE_BACKGROUND的flag
4.接收方:發(fā)送的廣播需要接收者有權(quán)限校驗椿肩,并且這個權(quán)限是系統(tǒng)定義并且安全級別為僅簽名 (signature)的
這個有點坑瞻颂,查看google的提交記錄,他們早期考慮是第三方也適用的郑象,只要這個廣播有權(quán)限校驗便不受限制贡这,后來
最后的版本添加了只有系統(tǒng)定義的權(quán)限才適用
/**
* 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.系統(tǒng)發(fā)送的部分廣播 :系統(tǒng)白名單的廣播不受影響
系統(tǒng)發(fā)送這些廣播的時候帶了 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.發(fā)送方:PendingIntent.getBroadcast
通過查看系統(tǒng)源碼邏輯(安全方面考慮),這種方式是顯式發(fā)送
@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.接收方:動態(tài)注冊
2.接收方:使用JobScheduler(網(wǎng)絡狀態(tài)等條件可替代部分廣播功能盖矫,注意國內(nèi)os對后臺特性的修改)
2.發(fā)送方:發(fā)送廣播時增加FLAG_RECEIVER_INCLUDE_BACKGROUND標志
3.接收方:添加權(quán)限校驗(對第三方應用不適用)
4.常駐后臺應用中轉(zhuǎn)(對第三方應用不適用)
技術(shù)細節(jié)與原理
限制隱式廣播的關鍵代碼邏輯如下 (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;
}
}
}
為什么動態(tài)注冊的廣播不受限制?
通過查看系統(tǒng)源碼BroadcastQueue的邏輯得知