前面我们分析了Android应用程序注册广播接收器的过程,接下来它还要等待ActivityManagerService将广播分发过来。ActivityManagerService是如何得到广播并把它分发出去的呢?
广播的发送者将广播发送到ActivityManagerService,ActivityManagerService接收到这个广播以后,就会在自己的注册中心查看有哪些广播接收器订阅了该广播,然后把这个广播逐一发送到这些广播接收器中,但是ActivityManagerService并不等待广播接收器处理这些广播就返回了,因此,广播的发送和处理是异步的。概括来说,广播的发送路径就是从发送者到ActivityManagerService,再从ActivityManagerService到接收者,这中间的两个过程都是通过Binder进程间通信机制来完成的。
发送广播
ContextWrapper.sendBroadcast
这个函数定义在frameworks/base/core/java/android/content/ContextWrapper.java文件中:
public class ContextWrapper extends Context {
Context mBase;
......
@Override
public void sendBroadcast(Intent intent) {
mBase.sendBroadcast(intent);
}
......
}
我们上文介绍了ContextWrapper类是Context类的封装类,ContextImpl是Context类的实现类。这里的成员变量mBase是一个ContextImpl实例,这里只简单地调用ContextImpl.sendBroadcast进一行操作。
ContextImpl.sendBroadcast
这个函数定义在frameworks/base/core/java/android/app/ContextImpl.java文件中:
public void sendBroadcast(Intent intent) {
............
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
//StrictMode下,对一些Action需要进行检查
intent.prepareToLeaveProcess(this);
//调用AMS中的接口
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch(RemoteException e) {
..............
}
}
这里的resolvedType表示这个Intent的MIME类型,如果没有设置这个Intent的MIME类型,resolvedType为null。接下来就调用ActivityManagerService的远程接口ActivityManagerProxy把这个广播发送给ActivityManagerService了。
ActivityManagerProxy.broadcastIntent
这个函数定义在frameworks/base/core/java/android/app/ActivityManagerNative.java文件中:
class ActivityManagerProxy implements IActivityManager
{
......
public int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
String requiredPermission, boolean serialized,
boolean sticky) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
......
mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
reply.readException();
int res = reply.readInt();
reply.recycle();
data.recycle();
return res;
}
......
}
这里主要是把要传递的参数封装好,然后通过Binder驱动程序进入到ActivityManagerService的broadcastIntent()方法中。
ActivityManagerService.broadcastIntent
这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中: public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
......
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
String requiredPermission, boolean serialized, boolean sticky) {
synchronized(this) {//verifyBroadcastLocked方法检查Broadcast中intent携带的信息是否有问题(不能携带文件描述符,避免安全隐患)
intent = verifyBroadcastLocked(intent);//同时检查intent的flag
final ProcessRecord callerApp = getRecordForAppLocked(caller);//获取调用方(发送广播)的ProcessRecord
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo,
resultCode, resultData, map, requiredPermission, serialized,
sticky, callingPid, callingUid);
Binder.restoreCallingIdentity(origId);
return res;
}
}
......
}
这里注意几个参数:
serialized:表示当前广播是否是order广播,true代表order广播(有序广播);
sticky:表示当前广播是否是sticky广播,true代表sticky广播(粘性广播);
broadcastIntent中对Broadcast对应的Intent进行一些检查后,调用broadcastIntentLocked进行实际的处理。
ActivityManagerService.broadcastIntentLocked
这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中,代码量较大,分段分析。
Part I
final int broadcastIntentLocked(.....) {
intent = new Intent(intent);
// By default broadcasts do not go to stopped apps.
// Android系统对所有app的运行状态进行了跟踪
// 当应用第一次安装但未使用过,或从程序管理器被强行关闭后,将处于停止状态
// 在发送广播时,不管是什么广播类型,系统默认增加了FLAG_EXCLUDE_STOPPED_PACKAGES的flag
// 使得对于所有BroadcastReceiver而言,如果其所在进程的处于停止状态,该BroadcastReceiver将无法接收到广播
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
//大量条件检查,例如:
//有些Action只能由系统来发送;
//有些Action需要发送方申明了对应权限
...................
//某些Action,例如Package增加或移除、时区或时间改变、清除DNS、代理变化等,需要AMS来处理
//AMS需要根据Action,进行对应的操作
..................
}
1、排除Stopped状态的应用
// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
所有的广播Intent在这里都会默认加上这个标记,表示
所有的广播都不会发送到Stopped状态的应用,应该在发送的时候会检查应用当前的状态。
2、系统升级广播
// 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);
}
Intent.FLAG_RECEIVER_BOOT_UPGRADE是系统升级的flag,允许在系统启动前发送。只有注册了的接收者被调用,所有的BroadcastReceiver组件不会被加载。
3、处理受保护广播
// Verify that protected broadcasts are only being sent by system code,受保护的广播只能有系统发送
// and that system code is only sending protected broadcasts.
final String action = intent.getAction();
final boolean isProtectedBroadcast;
try {
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception", e);
return ActivityManager.BROADCAST_SUCCESS;
}
受保护广播是通过查询PMS得到的,如果是受保护广播,抛出异常后直接返回ActivityManager.BROADCAST_SUCCESS,不会进行下面的动作。
4、判断发送者是否是系统进程
final boolean isCallerSystem;
switch (UserHandle.getAppId(callingUid)) {//如果是root、system、phone、bluetooth、nfc等的UID,则广播不受限制
case Process.ROOT_UID:
case Process.SYSTEM_UID:
case Process.PHONE_UID:
case Process.BLUETOOTH_UID:
case Process.NFC_UID:
isCallerSystem = true;
break;
default:
isCallerSystem = (callerApp != null) && callerApp.persistent;
break;
}
isCallerSystem表示系统级用户发送的广播,这部分广播不受限制。
5、处理特定系统广播
if (action != null) {
switch (action) {
case Intent.ACTION_UID_REMOVED:
case Intent.ACTION_PACKAGE_REMOVED:
case Intent.ACTION_PACKAGE_CHANGED:
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
case Intent.ACTION_PACKAGES_SUSPENDED:
case Intent.ACTION_PACKAGES_UNSUSPENDED:
// Handle special intents: if this broadcast is from the package
// manager about a package being removed, we need to remove all of
// its activities from the history stack.
对于一些来自PMS的包状态的变化,AMS需要及时的处理相关的Activity,这里是因为AMS兼顾了所有的4大组件,当包的状态发生变化,AMS作为总管需要第一时间内处理完总管要做的事情,然后将对应的广播再转发给对应的应用。
发送广播的第一阶段主要工作有:
(1).根据广播对应的Intent中的信息,判断发送方是否有发送该广播的权限;
(2).检查发送的广播是否是一些特殊的系统广播,特别是从PackageManagerService中发出的有个安装的应用移除的广播,如果检测到,需要将这些包中的Activity从AMS的Activity栈中移除。
Part II
..............
// Add to the sticky list if requested.
// 处理粘性广播相关的内容
if (sticky) {
//检查是否有发送权限,在AndroidManifest.xml中必须声明android.permission.BROADCAST_STICKY权限
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
callingPid, callingUid)
!= PackageManager.PERMISSION_GRANTED) {
..................//抛出SecurityException异常
}
//粘性广播不能指定接收权限,即发送时不能有requiredPermission权限信息
if (requiredPermissions != null && requiredPermissions.length > 0) {
..............
return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
}
if (intent.getComponent() != null) {
//发送的intent里面不能指定接收者,即粘性广播不能指定接收方
............
}
// We use userId directly here, since the "all" target is maintained
// as a separate set of sticky broadcasts.
//当粘性广播是针对特定userId时,判断该粘性广播与系统保存的是否冲突
if (userId != UserHandle.USER_ALL) {
//取出发送给所有user的粘性广播,关于mStickyBroadcasts的介绍可参考上一篇博客
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
UserHandle.USER_ALL);
if (stickies != null) {
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list != null) {
int N = list.size();
int i;
for (i=0; i<N; i++) {
//发送给特定user的粘性广播,与发送给所有user的粘性广播,action一致时,发生冲突
if (intent.filterEquals(list.get(i))) {
//抛出异常
.............
}
}
}
}
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if (stickies == null) {
stickies = new ArrayMap<>();
//按userId保存粘性广播
//即一个user,可能有多个粘性广播
mStickyBroadcasts.put(userId, stickies);
}
//按照Action保存粘性广播
//即一个Action,可能对应中多个广播
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list == null) {
//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));
}
}
}
...............
broadcastIntentLocked方法第二部分主要是处理粘性广播的,判断发送粘性广播的条件是否满足,然后就粘性广播保存起来,将其保存到AMS的mStickyBroadcasts变量里面,上一篇博客中分析了mStickyBroadcasts是以用户id作为key保存的,首先取出当前用户的所有sticky广播,然后根据当前广播的action保存到action对应的List里面即可。里面还有一个细节,就是如果当前intent和list中的某个intent用filterEquals()比较相等,就直接替换掉以前的,否则直接添加到list末尾。
Part III
.............
// Figure out who all will receive this broadcast.弄清楚谁会接收这个广播
//receivers主要用于保存匹配当前广播的静态注册的BroadcastReceiver
//若当前广播是有序广播时,还会插入动态注册的BroadcastReceiver
List receivers = null;
//registeredReceivers用于保存匹配当前广播的动态注册的BroadcastReceiver
//BroadcastFilter中有对应的BroadcastReceiver的引用
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested