ActivityManagerService 发送广播
简述
AMS主要管理的四大组件,Activity,Service,Broadcast,ContentProvider。其中Activity和Service有一定类似,ContentProvider使用的场景比较少,广播的使用场景还是比较多的,常常用于跨进程通信,在系统中系统应用和系统通信使用的更多。
广播的使用非常方便,比直接使用aidl binder更加容易,所以一般如果通信频率不是非常高,都会通过广播来做进程间通信,我们这一节就来看一下app发送广播SystemServer做了什么。
注册广播
app开发注册广播有两个方案,静态注册和动态注册,静态注册通过AndroidManifest.xml,动态注册通过代码注册。
静态注册后AMS会通过collectReceiverComponents,通过PMS获取来获取静态广播注册的信息。
而动态注册则会记录在AMS侧。
app发送广播
app侧发送广播是从Context.sendBroadcast开始的。
1.1 Context.sendBroadcast
Intent内包含了广播的信息,这里通过binder调用了AMS的broadcastIntentWithFeature。
public void sendBroadcast(Intent intent, String receiverPermission) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
intent.prepareToLeaveProcess(this);
// 详见1.2
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, receiverPermissions,
null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
1.2 ActivityManagerService.broadcastIntentWithFeature
这里已经到了AMS侧。
加上了AMS的大锁,根据call进行鉴权,然后调用broadcastIntentLocked进行广播发送。
public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, String[] excludedPermissions,
String[] excludedPackages, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
intent = verifyBroadcastLocked(intent);
final ProcessRecord callerApp = getRecordForAppLOSP(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final ProcessRecord resultToApp = callerApp;
// 根据callUid鉴权,看是否有发送广播的权限。
enforceBroadcastOptionPermissionsInternal(bOptions, callingUid);
final long origId = Binder.clearCallingIdentity();
try {
// 调用broadcastIntentLocked,详见1.3
return broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
appOp, bOptions, serialized, sticky, callingPid, callingUid, callingUid,
callingPid, userId, BackgroundStartPrivileges.NONE, null, null);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
1.3 ActivityManagerService.broadcastIntentLocked
调用了traceBegin和traceEnd用于记录发送广播的时间,中间调用broadcastIntentLockedTraced。
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
@Nullable String callerFeatureId, Intent intent, String resolvedType,
ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions,
String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int realCallingUid, int realCallingPid, int userId,
BackgroundStartPrivileges backgroundStartPrivileges,
@Nullable int[] broadcastAllowList,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
final int cookie = BroadcastQueue.traceBegin("broadcastIntentLockedTraced");
// 详见1.4
final int res = broadcastIntentLockedTraced(callerApp, callerPackage, callerFeatureId,
intent, resolvedType, resultToApp, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, excludedPermissions, excludedPackages, appOp,
BroadcastOptions.fromBundleNullable(bOptions), ordered, sticky,
callingPid, callingUid, realCallingUid, realCallingPid, userId,
backgroundStartPrivileges, broadcastAllowList, filterExtrasForReceiver);
BroadcastQueue.traceEnd(cookie);
return res;
}
1.4 ActivityManagerService.broadcastIntentLockedTraced
这个方法很长,但是实际的逻辑很简单,主要是因为对很多特殊情况都做了特殊处理,所里导致方法很长。
前面做了一些权限检查,以及是否是系统发送广播,是否是受保护广播等。
如果是粘性广播,则由StickyBroadcast记录,有一个二级Map数据结构来记录。
然后需要计算广播等Receiver,分静态广播和动态广播,分别存储在receivers和registeredReceivers。
如果是无序广播且mEnableModernQueue为false,这个时候直接可以直接发送动态广播,然后后续发送静态广播。
否则后续根据优先级合并静态广播和动态广播然后一起入队列发送。
这里发送广播就是通过new BroadcastRecord,然后enqueueBroadcastLocked入队列。
final int broadcastIntentLockedTraced(ProcessRecord callerApp, String callerPackage,
@Nullable String callerFeatureId, Intent intent, String resolvedType,
ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions,
String[] excludedPermissions, String[] excludedPackages, int appOp,
BroadcastOptions brOptions, boolean ordered, boolean sticky, int callingPid,
int callingUid, int realCallingUid, int realCallingPid, int userId,
BackgroundStartPrivileges backgroundStartPrivileges,
@Nullable int[] broadcastAllowList,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
// Ensure all internal loopers are registered for idle checks
BroadcastLoopers.addMyLooper();
// ... 如果caller是沙盒应用,需要通过SdkSandboxManagerLocal判断是否可以发送广播。
// ...resultapp检查
// 复制intent
intent = new Intent(intent);
// 及时应用不能发送广播给及时应用(及时应用是指不用完整安装即可使用的应用,即快应用)
final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
if (callerInstantApp) {
intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
}
if (userId == UserHandle.USER_ALL && broadcastAllowList != null) {
Slog.e(TAG, "broadcastAllowList only applies when sending to individual users. "
+ "Assuming restrictive whitelist.");
broadcastAllowList = new int[]{};
}
// ...
// ...权限检查
// 验证受保护的广播是否仅由系统发送,以及系统是否仅发送受保护的播放
final boolean isProtectedBroadcast;
try {
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
} catch (RemoteException e) {
// ...
}
final boolean isCallerSystem;
switch (UserHandle.getAppId(callingUid)) {
case ROOT_UID:
case SYSTEM_UID:
case PHONE_UID:
case BLUETOOTH_UID:
case NFC_UID:
case SE_UID:
case NETWORK_STACK_UID:
isCallerSystem = true;
break;
default:
isCallerSystem = (callerApp != null) && callerApp.isPersistent();
break;
}
// 如果非系统应用,不能发送受保护广播
if (!isCallerSystem) {
if (isProtectedBroadcast) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " from pid="
+ callingPid + ", uid=" + callingUid;
Slog.w(TAG, msg);
throw new SecurityException(msg);
} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
if (callerPackage == null) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " from unknown caller.";
Slog.w(TAG, msg);
throw new SecurityException(msg);
} else if (intent.getComponent() != null) {
if (!intent.getComponent().getPackageName().equals(
callerPackage)) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " to "
+ intent.getComponent().getPackageName() + " from "
+ callerPackage;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
} else {
intent.setPackage(callerPackage);
}
}
}
boolean timeoutExempt = false;
if (action != null) {
// ...对特殊的一些action做操作,主要是一些PMS信息变更的广播,如app卸载移除,需要kill掉对应进程等。
}
final int callerAppProcessState = getRealProcessStateLocked(callerApp, realCallingPid);
if (sticky) {
// 粘性广播的权限检查。
if (userId != UserHandle.USER_ALL) {
// 如果不是发送给所有用户的广播,和现有的发送给所有用户的广播比较,防止冲突。
}
// 这里可以看到粘性广播的数据结构,userId为key,Value也是一个map。Value中key为action,value是StickyBroadcast列表。
// 这里就是把新的StickyBroadcast添加到数据结构中去。
ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId);
if (stickies == null) {
stickies = new ArrayMap<>();
mStickyBroadcasts.put(userId, stickies);
}
ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
if (list == null) {
list = new ArrayList<>();
stickies.put(intent.getAction(), list);
}
final boolean deferUntilActive = BroadcastRecord.calculateDeferUntilActive(
callingUid, brOptions, resultTo, ordered,
BroadcastRecord.calculateUrgent(intent, brOptions));
final int stickiesCount = list.size();
int i;
for (i = 0; i < stickiesCount; i++) {
if (intent.filterEquals(list.get(i).intent)) {
list.set(i, StickyBroadcast.create(new Intent(intent), deferUntilActive,
callingUid, callerAppProcessState));
break;
}
}
if (i >= stickiesCount) {
list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive, callingUid,
callerAppProcessState));
}
}
int[] users;
if (userId == UserHandle.USER_ALL) {
users = mUserController.getStartedUserArray();
} else {
users = new int[] {userId};
}
final int cookie = BroadcastQueue.traceBegin("queryReceivers");
List receivers = null;
// 找出谁会接受这个广播,registeredReceivers里面放动态注册的广播,receivers为静态广播。
List<BroadcastFilter> registeredReceivers = null;
// 这个标记静态注册的Recevier收不到
if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
// AndroidManifest里面的静态注册的广播
receivers = collectReceiverComponents(
intent, resolvedType, callingUid, users, broadcastAllowList);
}
if (intent.getComponent() == null) {
final PackageDataSnapshot snapshot = getPackageManagerInternal().snapshot();
if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
for (int i = 0; i < users.length; i++) {
if (mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
continue;
}
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(snapshot, intent,
resolvedType, false /*defaultOnly*/, users[i]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
} else if (registeredReceiversForUser != null) {
registeredReceivers.addAll(registeredReceiversForUser);
}
}
} else {
registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
resolvedType, false /*defaultOnly*/, userId);
}
}
BroadcastQueue.traceEnd(cookie);
final boolean replacePending =
(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
if (registeredReceivers != null && broadcastAllowList != null) {
for (int i = registeredReceivers.size() - 1; i >= 0; i--) {
final int owningAppId = UserHandle.getAppId(registeredReceivers.get(i).owningUid);
if (owningAppId >= Process.FIRST_APPLICATION_UID
&& Arrays.binarySearch(broadcastAllowList, owningAppId) < 0) {
registeredReceivers.remove(i);
}
}
}
filterNonExportedComponents(intent, callingUid, callingPid, registeredReceivers,
mPlatformCompat, callerPackage, resolvedType);
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
// mEnableModernQueue是一个配置项,如果为true会使用BroadcastQueueModernImpl,否则会使用BroadcastQueueImpl
if (!ordered && NR > 0 && !mEnableModernQueue) {
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
// 如果mEnableModernQueue为false,广播队列有四个
// BROADCAST_QUEUE_FG,BROADCAST_QUEUE_BG,BROADCAST_QUEUE_BG_OFFLOAD,BROADCAST_QUEUE_FG_OFFLOAD
// 分别是前台队列,后台队列,前台负载队列,后台负载队列,会有不同的优先级,这里会通过intent的flag获取对应的队列。然后根据广播信息构建BroadcastRecord,放到队列中。
// 如果mEnableModernQueue为true,就只有一个BroadcastQueueModernImpl,但实际BroadcastQueueModernImpl里面也会有多个不同的优先级队列。
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
registeredReceivers, resultToApp, resultTo, resultCode, resultData,
resultExtras, ordered, sticky, false, userId,
backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver,
callerAppProcessState);
queue.enqueueBroadcastLocked(r);
registeredReceivers = null;
NR = 0;
}
// Merge into one list.
int ir = 0;
if (receivers != null) {
// 防止刚安装的包立刻接收安装广播拉起自己来作为一个后门。
String skipPackages[] = null;
if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
|| Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
|| Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
Uri data = intent.getData();
if (data != null) {
String pkgName = data.getSchemeSpecificPart();
if (pkgName != null) {
skipPackages = new String[] { pkgName };
}
}
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
}
if (skipPackages != null && (skipPackages.length > 0)) {
for (String skipPackage : skipPackages) {
if (skipPackage != null) {
int NT = receivers.size();
for (int it=0; it<NT; it++) {
ResolveInfo curt = (ResolveInfo)receivers.get(it);
if (curt.activityInfo.packageName.equals(skipPackage)) {
receivers.remove(it);
it--;
NT--;
}
}
}
}
}
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
// 按照优先级排序,把receivers和registeredReceivers合并放到receivers。
while (it < NT && ir < NR) {
if (curt == null) {
curt = (ResolveInfo)receivers.get(it);
}
if (curr == null) {
curr = registeredReceivers.get(ir);
}
if (curr.getPriority() >= curt.priority) {
receivers.add(it, curr);
ir++;
curr = null;
it++;
NT++;
} else {
it++;
curt = null;
}
}
}
while (ir < NR) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.add(registeredReceivers.get(ir));
ir++;
}
// ...
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
// 如果有Receiver,放到对应队列。
BroadcastQueue queue = broadcastQueueForIntent(intent);
filterNonExportedComponents(intent, callingUid, callingPid, receivers,
mPlatformCompat, callerPackage, resolvedType);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
receivers, resultToApp, resultTo, resultCode, resultData, resultExtras,
ordered, sticky, false, userId,
backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver,
callerAppProcessState);
queue.enqueueBroadcastLocked(r);
} else {
// 没有Receiver对这个广播感兴趣,只记录一下。
if (intent.getComponent() == null && intent.getPackage() == null
&& (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);
}
}
return ActivityManager.BROADCAST_SUCCESS;
}
小结
AMS发送广播逻辑比较简单,主要是计算有多少Receivers,然后将广播信息封装至BroadcastRecord,放入队列。
处理方法较长,除去有一些权限检查,还有一些特殊Action的逻辑处理,但是总体逻辑还是比较简单。