【ActivityManagerService】Android 廣播拉起后臺進程那些事

??Android 8.0 版本以后,在Manifest中靜態注冊的廣播基本都被禁用了,第三方應用通過AndroidManifest.xml 監聽系統廣播拉起自身進程的情況被大大抑制,但依然有例外情況:

  • BOOT_COMPLETE 廣播可以拉起應用;
  • TIME_SET 廣播可以拉起應用;

??安卓官方提供了隱式廣播(Implicit Broadcast)例外情況的列表
??本文主要分析廣播如何能拉起應用的流程,以便對第三方應用后臺靜默啟動加以管控。

1. Android 系統發送廣播流程分析

broadcast.png

??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增加打點監控。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。