??Android 8.0 版本以后,在Manifest中靜態注冊的廣播基本都被禁用了,第三方應用通過AndroidManifest.xml 監聽系統廣播拉起自身進程的情況被大大抑制,但依然有例外情況:
- BOOT_COMPLETE 廣播可以拉起應用;
- TIME_SET 廣播可以拉起應用;
??安卓官方提供了隱式廣播(Implicit Broadcast)例外情況的列表。
??本文主要分析廣播如何能拉起應用的流程,以便對第三方應用后臺靜默啟動加以管控。
1. Android 系統發送廣播流程分析
??Android系統發送廣播的基本流程如上圖所示:
- 客戶端進程通過contenxt.sendBroadCast 接口發送廣播;具體實現在ContextImpl中;
- ContextImpl.sendBroadCast 通過AMS的binder 接口,最終調用broadcastIntentLocked方法發送廣播。
- broadcastIntentLocked 是個上千行的復雜方法,基本任務是要確定廣播是發送并行廣播還是串行廣播,并行和串行對應者BroadcastQueue的兩個廣播隊列。
- 如果接收端是動態注冊的廣播,默認走并行廣播隊列,效率高;
- 否則走ordered 串行廣播隊列,判斷權限,判斷是否需要拉起進程;
- 需要注意的是,客戶端不使用contenxt.sendOrderedBroadCast接口,也可能走BroadcastQueue的Ordered broadcast流程。
- 如果需要實例化客戶端進程,BroadcastQueue通過Ams的startProcessLocked接口拉起第三方進程。
2. AMS對隱式廣播的限制
??在上述流程中,在 BroadcastQueue.processNextBroadcast 過程中對隱式廣播加以限制:
- 如果廣播包含 Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND,直接拒絕發送;
Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND
- 如果廣播不包含接受者信息,并且沒有聲明為 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,也拒絕發送。
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
(((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;
}
3. AMS對STOPPED_PACKAGE的限制
??PackageManagerService中,對每一個app維護一個stopped標志位,AMS force-stop 強制關閉的應用也會被標記上stopped,應用啟動過以后會清除這個標志位。
adb shell am force-stop com.package.name
??AMS對stopped package 也有接收廣播的限制。
??AMS 在 broadcastIntentLocked 調用開始,對每一個Intent都設置了FLAG_EXCLUDE_STOPPED_PACKAGES,只有當 FLAG_INCLUDE_STOPPED_PACKAGES 標志也被設置的時候,Intent狀態才能生效。
/**
* If set, this intent will not match any components in packages that
* are currently stopped. If this is not set, then the default behavior
* is to include such applications in the result.
*/
public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
/**
* If set, this intent will always match any components in packages that
* are currently stopped. This is the default behavior when
* {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set.
If both of these
* flags are set, this one wins (it allows overriding of exclude for
* places where the framework may automatically set the exclude flag).
*/
public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;
IntentFilter 使用如下方法判斷是否需要把stopped package排除在外。
public boolean isExcludingStopped() {
return (mFlags &
(FLAG_EXCLUDE_STOPPED_PACKAGES | FLAG_INCLUDE_STOPPED_PACKAGES ) ) == FLAG_EXCLUDE_STOPPED_PACKAGES;
}
/**
* Set whether the given package should be considered stopped, making
* it not visible to implicit intents that filter out stopped packages.
*/
public void setPackageStoppedState(java.lang.String packageName, boolean stopped, int userId) throws android.os.RemoteException;
4. android.intent.action.TIME_SET 廣播的發送流程
??AlarmManagerService中的AlarmThread開機啟動監控線程,當系統時間變化時,kernel通知此線程發送TIME_SET 廣播:
Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
|Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
|Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
|Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
Intent.FLAG_RECEIVER_REPLACE_PENDING 替換隊列中之前的廣播
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT BOOT_COMPLETE 之前也注冊的receiver也能收到廣播
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND 后臺應用可以被拉起
Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS INSTANT_APPS 可以被拉起。
所以,起作用的是 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND 標志。
5. 拉起進程監控
?? 如果需要監控哪些應用被隱式廣播拉起,可以在BroadcastQueue.processNextBroadbroadcast 打點,監控所有通過廣播啟動的進程。
if ((r.curApp=mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
"broadcast",r.curComponent,
(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
可以在BroadcastQueue增加打點監控,或者在AMS startProcessLocked增加打點監控。