0%

Broadcast 源码分析

Android 广播机制介绍

类似于发布-订阅设计模式,Android 应用可以与其他应用相互收发广播消息,还可以接收 Android 系统的广播消息。例如,Android 系统会在系统启动或设备开始充电时发送广播。再比如,应用可以发送自定义广播来通知其他应用它们可能感兴趣的事件。

应用可以注册接收特定的广播,这样系统会在广播发出后传送给这些应用。

按发送方式分类,广播可分为有序广播、常规广播、本地广播;按定义方式分类,可分为系统广播、自定义广播。此外,广播接收器可分为清单声明的接收器与上下文注册的接收器。

接收广播

清单声明的接收器

如果在清单中声明广播接收器,系统会在广播发出后启动该应用。

使用方法:

  1. 在应用清单中指定 <receiver> 元素,<intent-filter> 指定订阅的广播操作。
  2. 创建 BroadcastReceiver 子类,实现 onReceive(Context, Intent) 方法。

系统软件包管理器会在应用安装时注册接收器。

上下文注册的接收器

使用方法:

  1. 创建 BroadcastReceiver 子类并实例化。
  2. 创建 IntentFilter 并调用 registerReceiver(BroadcastReceiver, IntentFilter) 注册接收器。只要注册上下文有效,接收器就会接收到广播。
  3. 如有需要,调用unregisterReceiver(android.content.BroadcastReceiver) 停止接收广播。

对进程状态的影响

BroadcastReceiver 的状态(无论它是否在运行)会影响其所在进程的状态,而其所在进程的状态又会影响它被系统终结的可能性。例如,当进程执行接收器(即当前在运行其 onReceive() 方法中的代码)时,它被认为是前台进程。除非遇到极大的内存压力,否则系统会保持该进程运行。

但是,一旦从 onReceive() 返回代码,BroadcastReceiver 就不再活跃。接收器的宿主进程变得与在其中运行的其他应用组件一样重要。如果该进程仅托管清单声明的接收器(这对于用户从未与之互动或最近没有与之互动的应用很常见),则从 onReceive() 返回时,系统会将其进程视为低优先级进程,并可能会将其终止,以便将资源提供给其他更重要的进程使用。

发送广播

Android 为应用提供三种方式来发送广播:

  • sendOrderedBroadcast(Intent, String) 方法发送有序广播。系统按接收器的优先级逐个顺序向接收器发送广播,上一个接收器处理完广播后才发送广播到下一个接收器。当接收器逐个顺序执行时,接收器可以向下传递结果,也可以完全中止广播,使其不再传递给其他接收器。接收器的运行顺序可以通过匹配的 intent-filter 的 android:priority 属性来控制;具有相同优先级的接收器将按随机顺序运行。

  • sendBroadcast(Intent) 方法发送常规广播。系统会按随机的顺序向所有接收器发送广播。这种方法效率更高,但也意味着接收器无法从其他接收器读取结果,无法传递从广播中收到的数据,也无法中止广播。

  • LocalBroadcastManager.sendBroadcast 方法发送本地广播。这种广播只会发送给与发送器位于同一应用中的接收器,适用于无需跨应用发送广播的场景。这种实现方法的效率更高(无需进行进程间通信),而且无需担心其他应用在收发您的广播时带来的任何安全问题。

当 BroadcastReceiver 接收到有序广播后,在 onReceive 方法内:

  • 使用 getResultCodegetResultData,getResultExtras 方法取得上个广播接收器返回的结果;
  • 使用 setResult(int, String, Bundle) 以及对应的 set 方法设置结果传递给下个广播接收器;
  • 使用 abortBroadcast() 方法来中止广播,使其不再传递给下个接收器。

源码分析

广播机制的“注册中心”,是由 ActivityManagerService 来担当的,这个系统组件负责处理广播接收器的注册请求,收取所有发送的广播,根据广播类型找到相应的接收器,最后将广播发送给接收器们处理。

注册接收器

清单声明的接收器(静态注册)

TODO

上下文注册的接收器(动态注册)

注册广播接收器时序图

应用调用 Activity/Service 的 registerReceiver 方法注册接收器,而 Activity/Service 都继承于 Context 抽象类,最终调用的实现在 ContextImpl.registerReceiverInternal 方法中。

准备工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context, int flags) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
final Intent intent = ActivityManager.getService().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
broadcastPermission, userId, flags);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}

mPackageInfo 是 LoadedApk 类型对象,在应用启动时创建。

如果 scheduler 参数没有指定 Handler, 会获取主线程的 Handler。

将广播接收器 receiver 封装成 LoadedApk.ReceiverDispatcher 对象,包含了传入的 BroadcastReceiver,调用者的 Context,用于处理的 Handler,主线程的 Instrumentation(用于测试)。这个 ReceiverDispatcher 对象包含一个实现了 IIntentReceiver AIDL 接口的 InnerReceivcer 对象 mIIntentReceiver

如果 mPackageInfo 对象和 context 参数都不为空,使用 LoadedApk.getReceiverDispatcher 方法获取该对象,同时在 LoadedApk 的 mReceivers 成员注册该 ReceiverDispatcher;否则,创建 ReceiverDispatcher 对象并使用 getIIntentReceiver() 方法返回其 InnerReceivcer 对象。

通过 Binder 调用 ActivityManagerService 的 registerReceiver 方法,传入封装的 InnerReceivcer 对象、 IntentFilter 和其他调用者相关的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags) {
......
synchronized(this) {
if (caller != null) {
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {...} // throw SecurityException
if (callerApp.info.uid != SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {...} // throw SecurityException
callingUid = callerApp.info.uid;
callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
......
Iterator<String> actions = filter.actionsIterator();
if (actions == null) {...} // 新建空白迭代器

首先使用 getRecordForAppLocked 获取调用者(caller)的 ProcessRecord,如果找不到这个应用,或者应用不在运行,则抛出 SecurityException。如果 callernull,则使用 Binder 获取其 UID 和 PID。

获取 IntentFilter 的 actions 迭代器(如果为空,就新建空白迭代器)。

匹配粘性广播

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    // Collect stickies of users
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
if (stickies != null) {
ArrayList<Intent> intents = stickies.get(action);
if (intents != null) {
if (stickyIntents == null) {
stickyIntents = new ArrayList<Intent>();
}
stickyIntents.addAll(intents);
}
}
}
}
} //离开临界区

ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
final ContentResolver resolver = mContext.getContentResolver();
// Look for any matching sticky broadcasts...
for (int i = 0, N = stickyIntents.size(); i < N; i++) {
Intent intent = stickyIntents.get(i);
......
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);
}
}
}

// The first sticky in the list is returned directly back to the client.
Intent sticky = allSticky != null ? allSticky.get(0) : null;
...
if (receiver == null) {
return sticky;
}

遍历 actions 迭代器,寻找全局和调用者所属用户的粘性广播列表中 Intent action 匹配的广播,将结果添加到 stickyIntents 列表。

离开临界区,如果获取到了匹配的粘性广播,遍历结果列表,使用 IntentFilter.match 方法进一步匹配 IntentFilter 的其他属性,将最终结果添加到 allSticky 列表。

如果有获取到粘性广播,将列表中的第一个粘性广播赋值给 sticky,准备之后返回给调用者。

如果广播接收器为 null,在这里直接将结果 sticky 返回。

注册广播接收器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
synchronized (this) {
if (callerApp != null && (callerApp.thread == null
|| callerApp.thread.asBinder() != caller.asBinder())) {
// Original caller already died
return null;
}
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
final int totalReceiversForApp = rl.app.receivers.size();
if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {...} // throw IllegalStateException
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
} else if (rl.uid != callingUid) {...
} else if (rl.pid != callingPid) {...
} else if (rl.userId != userId) {...} // throw IllegalArgumentException
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId, instantApp, visibleToInstantApps);
if (rl.containsFilter(filter)) { ... // 警告接收器已经注册
} else {
rl.add(bf);
... //debugCheck
mReceiverResolver.addFilter(bf);
}

// Enqueue broadcasts for all existing stickies that match
// this filter.
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);

final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, false, null, null, OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1, false,
false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}

return sticky;
}

首先检查调用者进程是否还存活,如果已死,则返回 null。

mRegisteredReceiveres 获取当前接收器对应的接收器列表(ReceiverList rl)。

1
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();

mRegisteredReceiveres 保存了所有注册的广播接收器,其 hash key为 当前接收器 IBinder 对象 IIntentReceiver,值为对应的接收器列表。ReceiverList 是 ArrayList<BroadcastFilter> 的子类,保存了调用者的 ProcessRecord、UID、PID、UserID 以及接收器 IIntentFilter 信息。

如果还没有对应的列表,就新建一个,加入到 mRegisteredReceiveres 中。如果有调用者应用信息,这时还要检查调用者应用注册的接收器总数是否达到上限,超限则抛出 IllegalStateException,没有超限则加入到调用者应用的接收器列表中。如果之前没有得到调用者应用信息,在这里就向接收器列表注册当前接收器的死亡回调,检查接收器所在进程是否存活,若死亡就返回 sticky

判断获取的接收器列表中,UID/PID/UserID 是否与调用者的对应值相同,不同则抛出 IllegalArgumentException。

将广播接收器的接收器列表、IntentFilter、调用者、权限等信息封装到 BroadcastFilter 对象中。检查具有相同 IntentFilter 的接收器是否已存在于接收器列表中。如果没有,就将 BroadcastFilter 加入到接收器列表和 mReceiverResolver 中,也就完成了广播接收器的注册。

上面注册结束以后,如果筛选出了与当前注册的 IntentFilter 匹配的粘性广播列表,就将所有这些粘性广播逐条发送给当前的接收器 receivers,可以看到 receivers 里面就只有之前创建的一个 BroadcastFilter。最后返回 sticky

总结一下,通过上下文注册的广播接收器会在这些地方留下记录:

  • AMS 的 mRegisteredReceivers 成员中注册 BroadcastFilter
  • ProcessRecord 的 receivers 成员中注册 BroadcastFilter
  • LoadedApk 的 mReceivers 成员中注册 ReceiverDispatcher
  • AMS 的 mReceiverResolver 成员中注册 BroadcastFilter

注销接收器

通过 registerReceiver 注册的接收器可以通过 unRegisterReceiver 方法来注销。

类似于其配对方法,实现位于 ContextImpl.unRegisterReceiver

1
2
3
4
5
6
7
8
9
public void unregisterReceiver(BroadcastReceiver receiver) {
if (mPackageInfo != null) {
IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
getOuterContext(), receiver);
try {
ActivityManager.getService().unregisterReceiver(rd);
} catch (RemoteException e) {...}
} else {...}
}

使用 forgetReceiverDispatcher 移除 LoadedApk 中注册的 ReceiverDispatcher,再交由 AMS 的同名方法继续处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public void unregisterReceiver(IIntentReceiver receiver) {
......
final long origId = Binder.clearCallingIdentity();
try {
boolean doTrim = false;

synchronized(this) {
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl != null) {
final BroadcastRecord r = rl.curBroadcast;
if (r != null && r == r.queue.getMatchingOrderedReceiver(r)) {
final boolean doNext = r.queue.finishReceiverLocked(
r, r.resultCode, r.resultData, r.resultExtras,
r.resultAbort, false);
if (doNext) {
doTrim = true;
r.queue.processNextBroadcast(false);
}
}

if (rl.app != null) {
rl.app.receivers.remove(rl);
}
removeReceiverLocked(rl);
if (rl.linkedToDeath) {
rl.linkedToDeath = false;
rl.receiver.asBinder().unlinkToDeath(rl, 0);
}
}
}

if (doTrim) {
trimApplications(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
return;
}

} finally {
Binder.restoreCallingIdentity(origId);
}
}

注销接收器也就是在之前注册的地方移除对应的记录,此外还要从广播队列中移除该接收器。具体来说:

  • mRegisteredReceivers 中,取得对应的接收器列表,再取得该列表中正在处理的广播,再取得该广播的广播队列,调用 BroadcastQueue.finishReceiverLocked 方法
  • 从 ProcessRecord 的 receivers 成员中注销接收器;
  • 调用 removeReceiverLocked 方法,从 mRegisteredReceivers 中注销接收器,从 mReceiverResolver 中移除 BroadcastFilter。

发送广播

加入广播到 BroadcastQueue

应用调用 Context 类的 sendBroadcast 系列方法,其实现位于 ContextImpl 中,最终调用 AMS 的 broadcastIntent 方法。

初始验证

1
2
3
4
5
6
7
8
9
10
11
12
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
intent = verifyBroadcastLocked(intent);
...... // 准备调用 broadcastIntentLocked 方法
}
}

verifyBroadcastLocked 会判断广播的 Intent 是否合法:

  • 广播 Intent 不能包含文件描述符;
  • 如果没有设置 FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOTFLAG_RECEIVER_REGISTERED_ONLY 标志,不能在系统启动完成前广播;
  • 不能设置 FLAG_RECEIVER_BOOT_UPGRADE 标志;
  • 如果设置了 FLAG_RECEIVER_FROM_SHELL 标志,发送者必须以 root 或 shell 用户运行,否则该标志会被移除。

最后调用 broadcastIntentLocked 方法。这个方法很长,分段来看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
int realCallingPid, int userId, boolean allowBackgroundActivityStarts) {
intent = new Intent(intent);
......

// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
// If we have not finished booting, don't allow this to launch new processes.
if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_NON_FULL, "broadcast", callerPackage);

// Make sure that the user who is receiving this broadcast or its parent is running.
// If not, we will just skip it. Make an exception for shutdown broadcasts, upgrade steps.
if (userId != UserHandle.USER_ALL && !mUserController.isUserOrItsParentRunning(userId)) {
if ((callingUid != SYSTEM_UID
|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
Slog.w(TAG, "Skipping broadcast of " + intent
+ ": user " + userId + " and its parent (if any) are stopped");
return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
}
}

复制一份 Intent, 设置属性,使广播不能发送到停止的应用,不能在还没有启动完成时启动新进程。

判断广播可以送达的用户。广播不能向没有在运行的用户应用发送。

后台执行限制相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
final String action = intent.getAction();
BroadcastOptions brOptions = null;
if (bOptions != null) {
brOptions = new BroadcastOptions(bOptions);
if (brOptions.getTemporaryAppWhitelistDuration() > 0) {...}
if (brOptions.isDontSendToRestrictedApps()
&& !isUidActiveLocked(callingUid)
&& isBackgroundRestrictedNoCheck(callingUid, callerPackage)) {
Slog.i(TAG, "Not sending broadcast " + action + " - app " + callerPackage
+ " has background restrictions");
return ActivityManager.START_CANCELED;
}
if (brOptions.allowsBackgroundActivityStarts()) {...}
}
}

如果传入了 bOptions 参数,进行与后台执行限制相关的权限检查:

  • 不具有 CHANGE_DEVICE_IDLE_TEMP_WHITELIST 的进程不允许发送广播,并抛出 SecurityException;
  • 应用有后台限制,则广播不会被发送;
  • 广播选项允许后台 Activity 启动,并且发送者不具有 START_ACTIVITIES_FROM_BACKGROUND 权限,不允许发送广播,抛出 SecurityException。

Q: 带 options 参数的 sendBroadcast 方法带有 @SystemApi 装饰器,不应由开发者主动调用,那系统是如何调用该方法,添加所需的 options 参数呢?

判断与处理受保护广播

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
final boolean isProtectedBroadcast;
try {
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
} catch (RemoteException e) {...}

final boolean isCallerSystem;
// 判断发送者是否是系统进程
switch (UserHandle.getAppId(callingUid)) {...}

// First line security check before anything else: stop non-system apps from
// sending protected broadcasts.
if (!isCallerSystem) {
if (isProtectedBroadcast) {
......
throw new SecurityException(msg);

} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {...}
}

检查广播是否为受保护广播:只有系统能发送受保护广播,系统只能发送受保护广播。除了一个例外:为了兼容性原因,允许应用发送配置和更新应用微件(App Widget)的广播到自身的应用微件。

对一些系统广播(添加删除应用、时间更改、网络变更等)进行处理(杀进程、通知电池服务等),此处源码略过不表。

处理粘性广播

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
if (sticky) {
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,...)...) {...}
if (requiredPermissions != null && requiredPermissions.length > 0) {...}
if (intent.getComponent() != null) {...}
// We use userId directly here, since the "all" target is maintained
// as a separate set of sticky broadcasts.
if (userId != UserHandle.USER_ALL) {
// But first, if this is not a broadcast to all users, then
// make sure it doesn't conflict with an existing broadcast to
// all users.
......
}
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if (stickies == null) {
stickies = new ArrayMap<>();
mStickyBroadcasts.put(userId, stickies);
}
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list == null) {
list = new ArrayList<>();
stickies.put(intent.getAction(), list);
}
final int stickiesCount = list.size();
int i;
for (i = 0; i < stickiesCount; i++) {
if (intent.filterEquals(list.get(i))) {
// This sticky already exists, replace it.
list.set(i, new Intent(intent));
break;
}
}
if (i >= stickiesCount) {
list.add(new Intent(intent));
}
}

这一段处理粘性广播(Sticky Broadcast)。

所有粘性广播列表保存在 AMS 的 mStickyBroadcasts 成员中。最外层的 SparseArray 以 user ID 为 key,中层的 ArrayMap 以 Intent 的 action 为 key,相同 user ID 、相同 action 的 Intent 保存在 ArrayList 中。

1
final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts = ...;

首先检查权限:

  • 发送者没有 BROADCAST_STICKY 权限,则抛出 SecurityException;
  • 如果 requiredPermissions 非空,则广播发送中止,因为粘性广播不能规定接收器权限。
  • 如果广播的 Intent 指定了特定的组件作为接收器,则抛出 SecurityException,因为粘性广播不支持指定接收器。

如果广播不是针对所有用户,遍历针对所有用户的粘性广播(全局粘性广播)列表,检查是否与已有的全局广播冲突。如果有冲突,抛出 IllegalArgumentException。

最后,获取广播面向的用户对应的粘性广播列表,如果已有广播的 Intent filter 与当前广播相同,则用当前广播替换,否则,将当前广播加入该列表。

寻找广播接收器

1
2
3
4
5
6
7
8
int[] users;
if (userId == UserHandle.USER_ALL) {
// Caller wants broadcast to go to all started users.
users = mUserController.getStartedUserArray();
} else {
// Caller wants broadcast to go to one specific user.
users = new int[] {userId};
}

初始化广播将要发送给的用户列表:

  • 如果指定发送给所有用户,就将所有已在运行的用户加入列表;
  • 如果指定给特定用户,就只加入该用户到列表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Figure out who all will receive this broadcast.
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
for (int i = 0; i < users.length; i++) {
if (mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
continue;
}
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, users[i]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
} else if (registeredReceiversForUser != null) {
registeredReceivers.addAll(registeredReceiversForUser);
}
}
} else {
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, userId);
}
}

如果广播没有指定仅发送给上下文注册的接收器(通过设置 FLAG_RECEIVER_REGISTERED_ONLY),就调用 collectReceiverComponents 方法获取清单注册的接收器。

如果广播没有指定接收器的组件名,就获取接收器列表:如果指定发送给所有用户,遍历之前创建的正在运行用户列表,将每个用户的接收器加入到 registeredReceivers 列表;否则,将指定用户的接收器加入该列表。

发送常规广播

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
final boolean replacePending =
(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
+ " replacePending=" + replacePending);

int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
resultCode, resultData, resultExtras, ordered, sticky, false, userId,
allowBackgroundActivityStarts, timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending
&& (queue.replaceParallelBroadcastLocked(r) != null);
// Note: We assume resultTo is null for non-ordered broadcasts.
if (!replaced) {
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}

如果当前广播为常规广播,上下文注册的接收器列表非空,进行以下操作:

  1. 通过 broadcastQueueForIntent 方法,获取对应的广播队列。将广播封装到 BroadcastRecord 对象中。
  2. 如果广播(通过设置 FLAG_RECEIVER_REPLACE_PENDING) 要求顶替尚未发送的同类广播,就调用 replaceParallelBroadcastLocked 方法,尝试寻找广播队列中的同类广播并将其替换。否则,使用 enqueueParallelBroadcastLocked 将广播加入到广播队列,再使用 scheduleBroadcastsLocked 安排发送广播。
  3. 最后将上下文注册的接收器列表 registeredReceivers 设为 null,以避免下一步重复操作。

发送其余广播

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Merge into one list.
int ir = 0;
if (receivers != null) {
// 不允许应用接收自身的 PACKAGE_ADDED 广播,以免其利用该广播自启动。
......

int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
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) {
// Insert this broadcast record into the final list.
receivers.add(it, curr);
ir++;
curr = null;
it++;
NT++;
} else {
// Skip to the next ResolveInfo in the final list.
it++;
curt = null;
}
}
}
while (ir < NR) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.add(registeredReceivers.get(ir));
ir++;
}

上面将动态接收器的常规广播记录加入广播队列,剩下待处理的还有静态接收器的常规广播,以及有序广播(动态和静态接收器的)。

如果广播类型为 PACKAGE_ADDED / PACKAGE_RESTARTED / PACKAGE_DATA_CLEARED / EXTERNAL_APPLICATIONS_AVAILABLE,则从接收器列表中移除广播中提到的应用,以免其利用该广播进行自启。

将动态接收器列表 registeredReceivers 按优先级顺序合并到静态接收器列表 receivers 中。现在这个列表中包含了来自动态接收器的 BroadcastFilter 对象,和来自静态接收器的 ResolveInfo 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
......

if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId,
allowBackgroundActivityStarts, timeoutExempt);

if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);

final BroadcastRecord oldRecord =
replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
if (oldRecord != null) {
// Replaced, fire the result-to receiver.
if (oldRecord.resultTo != null) {
final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
try {
oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
oldRecord.intent,
Activity.RESULT_CANCELED, null, null,
false, false, oldRecord.userId);
} catch (RemoteException e) {...}
}
} else {
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
} else {
// There was nobody interested in the broadcast, but we still want to record
// that it happened.
if (intent.getComponent() == null && intent.getPackage() == null
&& (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
// This was an implicit broadcast... let's record it for posterity.
addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);
}
}

return ActivityManager.BROADCAST_SUCCESS;

类似之前的操作,获取广播队列,封装广播对象,尝试替换未发送的重复广播,或者加入广播到队列并安排发送。替换广播后要调用 performReceiveLocked 方法。

如果隐式广播没有找到接收器,调用 addBroadcastStatLocked 记录这次广播。

最后,返回广播成功的标志。

相关对象与方法

BroadcastQueue

数据存储

BroadcastQueue 对象分别存储了两种广播记录的队列,并支持对广播队列的增删改查操作。

动态注册的常规广播记录存储在 ArrayList<BroadcastRecord> mParallelBroadcasts 中。enqueueParallelBroadcastLockedreplaceParallelBroadcastLocked 方法实际上就是对该列表的操作。

其余广播存储在 BroadcastDispatcher mDispatcher 对象中,并由该对象负责管理。队列的实际存储位置是 BroadcastDispatcher.mOrderedBroadcasts,同样是 ArrayList<BroadcastRecord> 类型。enqueueOrderedBroadcastLockedreplaceOrderedBroadcastLocked 方法就是对该列表的操作。注意这里的变量和方法名令人困惑:这个广播队列不仅储存了有序广播,也储存了静态注册的常规广播。

通过 Handler 触发广播发送

BroadcastHandler 子类重载了 Handler 的 HandleMessage 方法,当接到 BROADCAST_INTENT_MSG 时执行 processNextBroadcast 方法,接到 BROADCAST_TIMEOUT_MSG 时执行 broadcastTimeoutLocked 方法。这个子类的实例作为成员变量 mHandler 出现,对应的 Looper 由 AMS 的 mHandlerThread 持有。

scheduleBroadcastsLocked 方法安排广播发送,实际上就是向 mHandler 发送 BROADCAST_INTENT_MSG,并设置 mBroadcastScheduled = true 以避免重复发送该消息。

处理广播队列

发送常规广播时序图

发送有序广播时序图

processNextBroadcast 方法获取 AMS 的锁,再执行 processNextBroadcastLocked 方法。

这个方法是广播队列处理的核心,很长,分段来看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
......
if (fromMsg) {
mBroadcastsScheduled = false;
}
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
...
final int N = r.receivers.size();
...
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
...
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
...
}

首先发送动态接收器的常规广播记录。一个个取出 mParallelBroadcasts 存储的常规广播记录,遍历其接收器,调用 deliverToRegisteredReceiverLocked 方法向接收器发送广播。最后调用 addBroadcastToHistoryLocked 方法记录广播历史。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (mPendingBroadcast != null) {
boolean isDead;
if (mPendingBroadcast.curApp.pid > 0) {
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(
mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.isCrashing();
}
} else {
final ProcessRecord proc = mService.mProcessList.mProcessNames.get(
mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
isDead = proc == null || !proc.pendingStart;
}
if (!isDead) {
// It's still alive, so keep waiting
return;
} else {
...
mPendingBroadcast.state = BroadcastRecord.IDLE;
mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
}
}

接下来处理其余广播。mPendingBroadcast 记录当前正在处理的广播。如果该广播不为空,判断正在处理该广播的进程是否已退出。如果没有退出,就直接返回,以继续等待该进程。如果已退出,表明该应用已完成处理这个广播,更改该广播的状态为空闲,更新其下个接收器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
boolean looped = false;

do { // while (r == null);
final long now = SystemClock.uptimeMillis();
r = mDispatcher.getNextBroadcastLocked(now);

if (r == null) {
.....// No more broadcasts are deliverable right now, so all done!
return;
}

boolean forceReceive = false;

// Ensure that even if something goes awry with the timeout
// detection, we catch "hung" broadcasts here, discard them,
// and continue to make progress.
//
// This is only done if the system is ready so that early-stage receivers
// don't get executed with timeouts; and of course other timeout-
// exempt broadcasts are ignored.
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
...
broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}

if (r.state != BroadcastRecord.IDLE) {
...
return;
}

进入循环,这个循环用来搜索下一个需要发送的广播并将其取到变量 r,只有当 r != null 时才会退出循环。

调用 mDispatcher.getNextBroadcastLocked 方法获取下一个要处理的广播。如果没有,就直接返回。

检查广播处理是否超时。AMS 转发广播时会将当前时间记录在 BroadcastRecord 中,如果这个广播任务不能在 2 * mConstants.TIMEOUT * numReceivers 时间内完成(默认 TIMEOUT 值为 10 秒),就调用 broadcastTimeoutLocked 方法强制完成广播,将其状态设为 IDLE。如果这个广播的状态不是 IDLE,在这里直接返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// In do {...} while (r == null)
// Is the current broadcast is done for any reason?
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
// Send the final result if requested
if (r.resultTo != null) {
boolean sendResult = true;

// if this was part of a split/deferral complex, update the refcount and only
// send the completion when we clear all of them
if (r.splitToken != 0) {
int newCount = mSplitRefcounts.get(r.splitToken) - 1;
if (newCount == 0) {
...// done! clear out this record's bookkeeping and deliver
mSplitRefcounts.delete(r.splitToken);
} else {
// still have some split broadcast records in flight; update refcount
// and hold off on the callback
...
sendResult = false;
mSplitRefcounts.put(r.splitToken, newCount);
}
}
if (sendResult) {
try {
...
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
// Set this to null so that the reference
// (local and remote) isn't kept in the mBroadcastHistory.
r.resultTo = null;
} catch (RemoteException e) {...}
}
}
......
// ... and on to the next...
addBroadcastToHistoryLocked(r);
if (r.intent.getComponent() == null && r.intent.getPackage() == null
&& (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
// This was an implicit broadcast... let's record it for posterity.
mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
}
mDispatcher.retireBroadcastLocked(r);
r = null;
looped = true;
continue;
}

如果当前广播已经完成,进行如下处理:

  1. 如果要求发送广播结果,执行 performReceiveLocked 方法
  2. 将当前广播加入历史记录
  3. 调用 AMS 的 addBroadcastStatLocked 方法记录当前广播
  4. 调用 mDispatcher.retireBroadcastLocked 方法
  5. 结束本轮循环,判断下一个广播是否需要发送
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// do { ...
if (!r.deferred) {
final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
if (mDispatcher.isDeferringLocked(receiverUid)) {
...
// If this is the only (remaining) receiver in the broadcast, "splitting"
// doesn't make sense -- just defer it as-is and retire it as the
// currently active outgoing broadcast.
BroadcastRecord defer;
if (r.nextReceiver + 1 == numReceivers) {
...
defer = r;
mDispatcher.retireBroadcastLocked(r);
} else {
// Nontrivial case; split out 'uid's receivers to a new broadcast record
// and defer that, then loop and pick up continuing delivery of the current
// record (now absent those receivers).

// The split operation is guaranteed to match at least at 'nextReceiver'
defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
...
// Track completion refcount as well if relevant
if (r.resultTo != null) {
int token = r.splitToken;
if (token == 0) {
// first split of this record; refcount for 'r' and 'deferred'
r.splitToken = defer.splitToken = nextSplitTokenLocked();
mSplitRefcounts.put(r.splitToken, 2);
...
} else {
// new split from an already-refcounted situation; increment count
final int curCount = mSplitRefcounts.get(token);
...
mSplitRefcounts.put(token, curCount + 1);
...
}
}
}
mDispatcher.addDeferredBroadcast(receiverUid, defer);
r = null;
looped = true;
continue;
}
}
} while (r == null);

TODO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Get the next receiver...
int recIdx = r.nextReceiver++;

// Keep track of when this receiver started, and make sure there
// is a timeout message pending to kill it if need be.
......
final BroadcastOptions brOptions = r.options;
final Object nextReceiver = r.receivers.get(recIdx);

if (nextReceiver instanceof BroadcastFilter) {
// Simple case: this is a registered receiver who gets
// a direct call.
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
...
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
if (r.receiver == null || !r.ordered) {
// The receiver has already finished, so schedule to
// process the next one.
...
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
} else {...}
return;
}

结束循环,表明已经取到需要发送的广播。取这个广播的下一个需要发送的接收器。

如果这个接收器是 BroadcastFilter 的实例,表明这个接收器是动态注册的:

  1. 不需要启动相应的应用,直接使用 deliverToRegisteredReceiverLocked 方法发送广播到接收器。

  2. 如果广播接收器已完成处理(在上一步执行方法时使 r.receiver = null),或者不是有序广播,就安排处理下一个广播:将广播状态设为 IDLE,调用 scheduleBroadcastsLocked 发送处理广播队列的消息。

  3. 最后总是返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// Hard case: need to instantiate the receiver, possibly
// starting its application process to host it.
ResolveInfo info =
(ResolveInfo)nextReceiver;
ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);

boolean skip = false;
......
final int receiverUid = info.activityInfo.applicationInfo.uid;
...
String targetProcess = info.activityInfo.processName;
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid, false);
...... // 处理一些禁止后台启动的情况,将 skip 设为 true
if (skip) {
......
r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
r.manifestSkipCount++;
scheduleBroadcastsLocked();
return;
}
r.manifestCount++;

r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;

// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
r.curComponent.getPackageName(), false, r.userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {...}

// Is this receiver's application already running?
if (app != null && app.thread != null && !app.killed) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
maybeAddAllowBackgroundActivityStartsToken(app, r);
processCurBroadcastLocked(r, app, skipOomAdj);
return;
} catch (RemoteException e) { ...
} catch (RuntimeException e) {
// If some unexpected exception happened, just skip
// this broadcast.
...... // finishReceiverLocked; ScheduleBroadcastLocked; state = IDLE;
return;
}

// If a dead object exception was thrown -- fall through to
// restart the application.
}

// Not running -- get it started, to be executed when the app comes up.
if ((r.curApp=mService.startProcessLocked(...)) == null) {
// 应用启动失败,跳过广播
...... // finishReceiverLocked; ScheduleBroadcastLocked; state = IDLE;
return;
}

执行到这里,表明这个接收器是静态声明的,需要判断是否需要启动接收器所在应用来处理广播。

判断一些不适合启动接收器应用的情形,跳过当前广播记录。如果判定需要跳过广播(skip = true),安排下个广播发送并返回。

接下来,调用 PackageManagerService.setPackageStoppedState 方法,设置 Stopped State 为 false。表明这个软件包不能被停止。

如果接收者进程已运行,则调用 processCurBroadcastLocked 方法直接发送广播给该进程,然后返回。如果该过程中抛出 RuntimeException 异常,则跳过当前广播记录并返回。

如果接收者进程尚未启动,则调用 AMS 的 startProcessLocked 方法拉起该进程。如果启动失败,则跳过当前广播记录并返回。

1
2
3
4
    maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}

startProcessLocked 方法是异步的,这里并不会等待进程启动完成后返回,再处理广播。因此设定 mPendingBroadcast 值为当前正在处理的广播记录,mPendingBroadcastRecvIndex 为当前处理的接收者。到这里整个方法就结束了,而这个待处理广播的发送也不在这个方法内完成,而是在进程启动完成时通过 AMS 调用相应方法处理的。前面判断 mPendingBroadcast 不为 null 时直接返回,进程完成后才继续处理,保证了有序广播总是在前一个接收器处理完之后才向下个接收器发送。

进程启动完成时,ActivityThread 会调用 AMS 的 attachApplication 方法,进一步调用 BroadcastQueue.sendPendingBroadcastsLocked 方法,最终将广播发送到启动的进程。

进程启动接收广播时序图

发送广播到接收者

processCurBroadcastLocked

调用 ActivityThread.ApplicationThread 的 scheduleReceiver 方法,向接收者进程发送广播 Intent。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app, boolean skipOomAdj) throws RemoteException {
...... // 一些异常判断

r.receiver = app.thread.asBinder();
r.curApp = app;
app.curReceivers.add(r);
...... // 通知 AMS 进程状态和优先级变更

// Tell the application to launch this receiver.
r.intent.setComponent(r.curComponent);

boolean started = false;
try {
......
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
app.getReportedProcState());
...
started = true;
} finally {
if(!started) {...} // 接收者进程启动失败,从 ProcessRecord 中移除广播记录。
}
}
deliverToRegisteredReceiverLocked

检查广播发送者是否有权限发送这个广播,广播接收器是否有权限接收这个广播。如果没有相应权限,则设置该广播结束。否则,将 BroadcastRecord 的成员变量传入 performReceiveLocked 发送该广播。

performReceiveLocked

使用单向 Binder 调用异步地向接收器发送广播 Intent。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser)
throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
if (app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
try {
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
} catch (RemoteException ex) {...} //throw ex
} else {...} //throw RemoteException
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
  • 对于具有 ApplicationThread 的一般应用,通过 Binder 调用 scheduleRegisteredReceiver方法,进一步调用InnerReceiver.performReceive` 方法。
  • 对于没有 ProcessRecord 的系统进程,直接调用 InnerReceiver.performReceive 方法。
LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive

该方法是对其父类的 performReceive 方法的包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
......
if (intent == null || !mActivityThread.post(args.getRunnable())) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManager.getService();
...
args.sendFinished(mgr);
}
}
}

该方法初始化一个 LoadedApk.ReceiverDispatcher.Args 对象,这个类型是 BroadcastReceiver.PendingResult 的子类。通过 getRunnable 方法返回一个 Runnable,接下来在 if 语句中向 ActivityThread 的 Handler 发送该 Runnable。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public final Runnable getRunnable() {
return () -> {
final BroadcastReceiver receiver = mReceiver;
final boolean ordered = mOrdered;
......
final IActivityManager mgr = ActivityManager.getService();
final Intent intent = mCurIntent;
......
if (receiver == null || intent == null || mForgotten) {
if (mRegistered && ordered) {
...
sendFinished(mgr);
}
return;
}
try {
......
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);;
} catch (Exception e) {
if (mRegistered && ordered) {
...
sendFinished(mgr);
}
......
}
......
};
}

这个方法首先调用 setPendingResult 设置接收器收到的返回结果,接着执行接收器的 onReceive 方法。如果执行 onReceive 或者发送 Runnable 过程出现异常,就会调用 sendFinished 方法。这个方法传入广播的返回结果到 AMS 的 finishReceiver 方法,以通知 AMS 该有序广播处理完毕,继续处理下一个目标。

finishReceiverLocked
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
final int state = r.state;
final ActivityInfo receiver = r.curReceiver;
final long finishTime = SystemClock.uptimeMillis();
final long elapsed = finishTime - r.receiverTime;
r.state = BroadcastRecord.IDLE;
...... // 调整活动优先级

// If we're abandoning this broadcast before any receivers were actually spun up,
// nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
if (r.nextReceiver > 0) {
r.duration[r.nextReceiver - 1] = elapsed;
}
...... // 记录处理广播耗时过长的应用
r.receiver = null;
r.intent.setComponent(null);
if (r.curApp != null && r.curApp.curReceivers.contains(r)) {
r.curApp.curReceivers.remove(r);
}
if (r.curFilter != null) {
r.curFilter.receiverList.curBroadcast = null;
}
r.curFilter = null;
r.curReceiver = null;
r.curApp = null;
mPendingBroadcast = null;

r.resultCode = resultCode;
r.resultData = resultData;
r.resultExtras = resultExtras;
if (resultAbort && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {
r.resultAbort = resultAbort;
} else {
r.resultAbort = false;
}
...... // 处理 waitForServices
r.curComponent = null;

// We will process the next receiver right now if this is finishing
// an app receiver (which is always asynchronous) or after we have
// come back from calling a receiver.
return state == BroadcastRecord.APP_RECEIVE
|| state == BroadcastRecord.CALL_DONE_RECEIVE;
}

这个方法将 AMS.finishReceiver 方法传入的广播结果记录到对应的 BroadcastRecord 中,这样下个广播接收者就能收到更新的结果。

broadcastTimeoutLocked

这个方法排除一些特定场景的例外后,强制完成广播,触发 ANR。

排除的场景包括:

  1. 系统尚未启动完成;
  2. 这个广播豁免了超时;
  3. 广播启动了服务,而服务被延迟启动;
  4. 应用处于调试状态。

不同的广播队列超时时间不同,详见 broadcastQueueForIntent

ActivityThread

ApplicationThread.scheduleReceiver

将收到的广播结果封装到 ActivityThread.ReceiverData 对象中,该类型是 BroadcastReceiver.PendingResult 的子类。向 ActivityThread 的 Handler 发送 RECEIVER 消息和 ReceiverData 对象,Handler 收到消息后调用 handleReceiver 方法;

ApplicationThread.scheduleRegisteredReceiver

通过 updateProcessState 更新进程状态,再调用 InnerReceiver.performReceive 方法。

handleReceiver

这个方法在 ActivityThread 中定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void handleReceiver(ReceiverData data) {
......
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);

IActivityManager mgr = ActivityManager.getService();

Application app;
BroadcastReceiver receiver;
ContextImpl context;
...... // 获取 app, receiver, context
try {
......
receiver.setPendingResult(data);
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
...
data.sendFinished(mgr);
...
}
} finally {
sCurrentBroadcastIntent.set(null);
}

if (receiver.getPendingResult() != null) {
data.finish();
}
}

功能和代码都类似于 Args.getRunnable 返回的 Runnable 方法,设置 PendingResult,调用 onReceive 方法,并在方法完成后通知 AMS 完成广播。

BroadcastReceiver

BroadcastReceiver 接收到广播后,可以使用 getResult 获取上个接收器返回的结果,使用 setResult 系列方法来设置结果传给下个广播接收器,或者使用 abortBroadcast 方法中止广播向下一个接收器传递。这一系列方法操作的是 BroadcastReceiver.PendingResult 对象。

setPendingResult

设置 mPendingResult 变量的值。ActivityThead.handleReceiverArgs.getRunnable 方法发送广播时,调用该方法设置接收器结果。

完成广播

Args.getRunnablehandleReceiver 在广播处理完返回时,都会调用 PendingResult.finish 方法,该方法会调用 PendingResult.sendFinished 方法。如果处理广播中出现异常,则会直接调用 sendFinished 方法。

sendFinished 方法禁止广播结果传递文件描述符,接着传入广播结果(可能被接收器更改)到 AMS 的 finishReceiver 方法。

AMS.finishReceiver 方法找到广播所在广播队列及对应的广播记录,传入广播结果到 BroadcastQueue.finishReceiverLocked 方法。接着,根据其返回值判定是否调用 BroadcastQueue.processNextBroadcastLocked 方法推动下次广播发送。

broadcastQueueForIntent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
BroadcastQueue broadcastQueueForIntent(Intent intent) {
if (isOnOffloadQueue(intent.getFlags())) {
...
return mOffloadBroadcastQueue;
}

final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
...
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}

private boolean isOnOffloadQueue(int flags) {
return (mEnableOffloadQueue && ((flags & Intent.FLAG_RECEIVER_OFFLOAD) != 0));
}

该方法返回 Intent 对应的广播队列。AMS 中定义了三个广播队列:mFgBroadcastQueue, mBgBroadcastQueue, mOffloadBroadcastQueue,分别用于前台的接收器、后台的接收器,和“长”广播(例如 BOOT_COMPLETED,通过 FLAG_RECEIVER_OFFLOAD 标志来判定)。

这三个队列有不同的广播超时时间:前台广播是 10 秒,后台广播和长广播是 60 秒。

collectReceiverComponents

这个方法调用 PackageManagerService.queryIntentReceivers 方法,

mReceiverResolver.queryIntent

查询指定用户的接收器使用的是 mReceiverResolver.queryIntent 方法,TODO

Q&A

Q: 从未打开过的 APP(包括系统 APP)可以接收 ACTION_BOOT_COMPLETED 广播么?

A: Android 8.0 开始对后台执行进行限制,应用无法在清单注册一些隐式广播的接收器。但是,ACTION_BOOT_COMPLETED 广播被豁免这一限制。

从未打开的非系统应用在软件包管理器(PackageManager)中处于停止状态(stopped state)。只有具有 FLAG_INCLUDE_STOPPED_PACKAGES 广播才可以被停止状态的应用接收。

而系统广播 ACTION_BOOT_COMPLETE 不具有该标志,而是有 FLAG_EXCLUDE_STOPPED_PACKAGES 标志,这意味着该广播不能被从未打开过的应用接收,但是系统应用不受该限制,因此该广播可以被从未打开过的系统应用接收。

参考文献

  1. 广播概览, https://developer.android.com/guide/components/broadcasts?hl=zh_cn
  2. Android系统源码分析-Broadcast注册和注销, https://www.jianshu.com/p/882cf3911772
  3. Android系统源码分析-Broadcast发送, https://www.jianshu.com/p/c5323a22f3f3
  4. 安装APP后不启动,实现开机启动APP的Activity或者Service, https://segmentfault.com/a/1190000015851405

源代码

1
2
3
4
5
6
7
8
9
10
11
frameworks/base/core/java/android/content/Context.java
frameworks/base/core/java/android/content/BroadcastReceiver.java

frameworks/base/core/java/android/app/ContextImpl.java
frameworks/base/core/java/android/app/ActivityThread.java

frameworks/base/services/core/java/com/android/server/IntentResolver.java

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
frameworks/base/services/core/java/com/android/server/am/ReceiverList.java