【ActivityManagerService】Android
Android 8.0 版本以后,在Manifest中静态注册的广播基本都被禁用了,第三方应用通过AndroidManifest.xml 监听系统广播拉起自身进程的情况被大大抑制,但依然有例外情况:
- BOOT_COMPLETE 广播可以拉起应用;
- TIME_SET 广播可以拉起应用;
安卓官方提供了隐式广播(Implicit Broadcast)例外情况的列表。
本文主要分析广播如何能拉起应用的流程,以便对第三方应用后台静默启动加以管控。
1. Android 系统发送广播流程分析
broadcast.pngAndroid系统发送广播的基本流程如上图所示:
- 客户端进程通过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增加打点监控。