一 概述
广播 (Broadcast) 机制用于进程或线程间通信,广播分为广播发送和广播接收两个过程,其中广播接收者 BroadcastReceiver 是 Android 四大组件之一。BroadcastReceiver 分为两类:
- 静态广播接收者:通过 AndroidManifest.xml 的标签来声明的 BroadcastReceiver
- 动态广播接收者:通过 AMS.registerReceiver() 方式注册的 BroadcastReceiver,动态注册更为灵活,可在不需要时动态取消注册
PS:动态 BroadcastReceiver 比较简单,静态的就麻烦一些了,因为在广播发送之时,静态 receiver 所从属的进程可能还没有启动呢,这就需要先启动新的进程,费时费力。另一方面,有些时候用户希望广播能够按照一定顺序发送,为此,Android 又搞出了 ordered broadcast 的概念。
从广播发送方式可分为三类:
- 普通广播:sendBroadcast(Intent) 可并行处理,方法会按随机的顺序向所有接收器发送广播。这称为常规广播。这种方法效率更高,但也意味着接收器无法从其他接收器读取结果,无法传递从广播中收到的数据,也无法中止广播。
- 有序广播:sendOrderedBroadcast(Intent, String) 串行处理,一次向一个接收器发送广播。当接收器逐个顺序执行时,接收器可以向下传递结果,也可以完全中止广播,使其不再传递给其他接收器。接收器的运行顺序可以通过匹配的 intent-filter 的 android:priority 属性来控制;具有相同优先级的接收器将按随机顺序运行。
- Sticky广播:通过sendStickyBroadcast()发送
广播在系统中以 BroadcastRecord 对象来记录,该对象有几个时间相关的成员变量需要注意。
final class BroadcastRecord extends Binder {final ProcessRecord callerApp; // 广播发送者所在进程final String callerPackage; // 广播发送者所在包名// 包括动态注册的 BroadcastFilter 和静态注册的 ResolveInfofinal List receivers;final int callingPid; // 广播发送者 pidint nextReceiver; // 下一个被执行的接收者IBinder receiver; // 当前正在处理的接收者int anrCount; // 广播 ANR 次数long enqueueClockTime; // 入队列时间,伴随着 scheduleBroadcastsLockedlong dispatchTime; // 分发时间long dispatchClockTime;// 分发时间,伴随着 deliverToRegisteredReceiverLockedlong receiverTime; // 接收时间(首次等于 dispatchClockTime)long finishTime; // 广播完成时间,位于 addBroadcastToHistoryLocked 方法内
}
我们这一节主要分析广播的注册流程,相关代码路径如下:
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/BroadcastRecord.javaframeworks/base/core/java/android/content/BroadcastReceiver.java
frameworks/base/core/java/android/app/ContextImpl.java
frameworks/base/core/java/android/app/LoadedApk.java
frameworks/base/core/java/android/app/ActivityThread.java
二 动态BroadcastReceiver注册
时序图
用户调用registerReceiver()方法,而Activity/Service都间接继承于Context抽象类,真正干活是交给ContextImpl类。
2.1 ContextImpl.registerReceiver
frameworks/base/core/java/android/app/ContextImpl.java
@Override
public Intent registerReceiver(BroadcastReceiver receiver,IntentFilter filter) {return registerReceiver(receiver, filter, null, null);
}@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,String broadcastPermission, Handler scheduler) {return registerReceiverInternal(receiver, getUserId(), filter,broadcastPermission, scheduler, getOuterContext(), 0);
}
注册之时,用户会把一个自定义的 receiver 对象作为第一个参数传入。当然,用户的 receiver 都是继承于 BroadcastReceiver 的。
其中 broadcastPermission 拥有广播的权限控制,scheduler 用于指定接收到广播时 onRecive 执行线程,当 scheduler=null 则默认代表在主线程中执行,这也是最常见的用法。
使用过广播机制的同学,对这个 BroadcastReceiver 应该都不陌生,这里就不多说了。我们需要关心的是,这个 registerReceiverInternal() 内部还包含了什么重要的细节。
2.2 ContextImpl.registerReceiverInternal
private Intent registerReceiverInternal(BroadcastReceiver receiver,int userId, IntentFilter filter, String broadcastPermission,Handler scheduler, Context context, int flags) {// 获取 InnerReceiver, 基本思路是从 mReceivers 映射表中查找和// BroadcastReceiver 一一对应的 ReceiverDispatcher// 进而获取 InnerReceiver. 如果没有则新建.IIntentReceiver rd = null;if (receiver != null) {if (mPackageInfo != null && context != null) {if (scheduler == null) {scheduler = mMainThread.getHandler();}// 查找和 context 对应的“子哈希表”里的 ReceiverDispatcher// 如果找不到,就重新 new 一个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 {// AMS.registerReceiver 注册广播.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();}
}
ContextImpl.registerReceiver 过程:
1.根据 BroadcastReceiver 获取对应的 ReceiverDispatcher
- 先根据 LoadedApk.getReceiverDispatcher,从 ArrayMap<BroadcastReceiver, ReceiverDispatcher> 映射表中得到和 BroadcastReceiver 对应的 ReceiverDispatcher
- 如果 LoadedApk 为空,则新建 LoadedApk.ReceiverDispatcher
2.执行 ReceiverDispatcher.getIIntentReceiver() 得到 InnerReceiver (即 IIntentReceiver.Stub )
3.调用 AMS.registerReceiver 注册广播
请大家注意那个 rd 对象(IIntentReceiver)。我们知道,在 Android 架构中,广播动作最终其实都是由 AMS 递送出来的。AMS 利用 binder 机制,将语义传递给各个应用进程,应用进程再辗转调用到 receiver 的 onReceive(),完成这次广播。而此处的 rd 对象正是承担“语义传递工作“的 binder 实体。
为了管理这个重要的 binder 实体,Android 搞出了一个叫做 ReceiveDispatcher 的类。该类的定义截选如下
2.3 LoadedApk.ReceiveDispatcher
LoadedApk.java
static final class ReceiverDispatcher {final static class InnerReceiver extends IIntentReceiver.Stub {final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;final LoadedApk.ReceiverDispatcher mStrongRef;InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);mStrongRef = strong ? rd : null;}// 核心回调函数@Overridepublic void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered,boolean sticky, int sendingUser) {final LoadedApk.ReceiverDispatcher rd;if (intent == null) {rd = null;} else {rd = mDispatcher.get();}......if (rd != null) {// 调用 LoadedApk.ReceiverDispatcher 的 performReceive 方法rd.performReceive(intent, resultCode, data, extras,ordered, sticky, sendingUser);} else {......}}}// 请注意这个域!它就是传到外面的rdfinal IIntentReceiver.Stub mIIntentReceiver;@UnsupportedAppUsage// 每一个 ReceiverDispatcher 对应一个 BroadcastReceiverfinal BroadcastReceiver mReceiver;final Context mContext;final Handler mActivityThread;final Instrumentation mInstrumentation;final boolean mRegistered;final IntentReceiverLeaked mLocation;RuntimeException mUnregisterLocation;boolean mForgotten;final class Args extends BroadcastReceiver.PendingResult {private Intent mCurIntent;private final boolean mOrdered;private boolean mDispatched;private boolean mRunCalled;public Args(Intent intent, int resultCode, ......) {super(resultCode, resultData, resultExtras, ......);mCurIntent = intent;mOrdered = ordered;}
// 这个 getRunnable 返回的 Runnable,会被发送到主线程的消息队列里执行public final Runnable getRunnable() {return () -> {final BroadcastReceiver receiver = mReceiver;final boolean ordered = mOrdered;...... try {ClassLoader cl = mReceiver.getClass().getClassLoader();intent.setExtrasClassLoader(cl);intent.prepareToEnterProcess();setExtrasClassLoader(cl);receiver.setPendingResult(this);// 关键调用:回调 receiver 的 onReceive 方法receiver.onReceive(mContext, intent);} catch (Exception e) {......}if (receiver.getPendingResult() != null) {finish();}};}}// ReceiverDispatcher 构造函数ReceiverDispatcher(BroadcastReceiver receiver, Context context,Handler activityThread, Instrumentation instrumentation,boolean registered) {if (activityThread == null) {throw new NullPointerException("Handler must not be null");}// 生成一个 InnerReceivermIIntentReceiver = new InnerReceiver(this, !registered);mReceiver = receiver;mContext = context;mActivityThread = activityThread;mInstrumentation = instrumentation;mRegistered = registered;mLocation = new IntentReceiverLeaked(null);mLocation.fillInStackTrace();}......// 返回这个 mIIntentReceiverIIntentReceiver getIIntentReceiver() {return mIIntentReceiver;}......public void performReceive(Intent intent, int resultCode,String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {// 把参数封装到 Args 中final Args args = new Args(intent, resultCode, data, extras,ordered, sticky, sendingUser);......// 通过 args 获取一个 Runnable,并把这个 Runnable post 到 主线程的消息队列// 其中的 mActivityThread 是一个 Handler,默认属于主线程if (intent == null || !mActivityThread.post(args.getRunnable())) {......}}
}
这样看来,“动态注册的 BroadcastReceiver ” 和 “ ReceiverDispatcher 节点” 具有一一对应的关系。示意图如下:
一个应用里可能会注册多个动态 receiver,所以这种一一对应关系最好整理成表,这个表就位于 LoadedApk 中。前文 mPackageInfo.getReceiverDispatcher() 一句中的 mPackageInfo 就是 LoadedApk 对象。
在 Android 的架构里,应用进程里是用 LoadedApk 来对应一个 apk 的,进程里加载了多少个 apk,就会有多少 LoadedApk。每个 LoadedApk 里会有一张 “关于本 apk 动态注册的所有 receiver ” 的哈希表(mReceivers)。当然,在 LoadedApk 初创之时,这张表只是个空表。
mReceivers 表的定义如下:
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers = new ArrayMap<>();
该表的 key 项是我们比较熟悉的 Context,也就是说可以是 Activity、Service 或 Application。而 value 项则是另一张“子哈希表”。这是个 “表中表” 的形式。
言下之意就是,每个 Context(比如一个 activity),是可以注册多个 receiver 的,这个很好理解。mReceivers 里的“子哈希表”的 key 值为 BroadcastReceiver,value 项为 ReceiverDispatcher,示意图如下:
接下来我们继续看 LoadedApk.getReceiverDispatcher
2.3.1 LoadedApk.getReceiverDispatcher
frameworks/base/core/java/android/app/LoadedApk.java
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) {synchronized (mReceivers) {LoadedApk.ReceiverDispatcher rd = null;ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;if (registered) {// 这个 mReceivers 就是我们上节说的哈希表map = mReceivers.get(context);if (map != null) {rd = map.get(r);}}// 如果没有对应的 ReceiverDispatcher,那么就创建一个if (rd == null) {rd = new ReceiverDispatcher(r, context, handler,instrumentation, registered);if (registered) {if (map == null) {map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();mReceivers.put(context, map);}map.put(r, rd);}}rd.mForgotten = false;return rd.getIIntentReceiver();}
}
ReceiverDispatcher 作用小结:
- 它是负责将广播 Broadcast 派发给 BroadcastReceiver 执行的广播派发器
- BroadcastReceiver 和 ReceiverDispatcher 是一一对应的,一个广播接收器对应一个广播派发器
- 它主要是通过内部类 InnerReceiver 和 Args 执行广播派发过程
2.4 AMS.ReceiverList
我们继续看 registerReceiverInternal() 的后半部分,最终调用到了 AMS 的 registerReceiver。
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();
}
registerReceiver() 函数的 filter 参数指明了 receiver 对哪些 intent 感兴趣。
对同一个 BroadcastReceiver 对象来说,可以注册多个感兴趣的 filter,就好像声明静态 receiver 时,也可以为一个 receiver 编写多个 <intent-filter> 一样。这些 IntentFilter 信息会汇总到 AMS 的 mRegisteredReceivers 表中。在 AMS 端,我们可以这样访问相应的汇总表:
ActivityManagerService.java
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();// 这里的 receiver 就是应用进程中传递过来的 IIntentReceiver
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
其中的 receiver 参数为 IIntentReceiver 类型,正对应着 ReceiverDispatcher 中那个 binder 实体。也就是说,每个客户端的 ReceiverDispatcher,会对应 AMS 端的一个 ReceiverList。
ReceiverList 的定义截选如下:
ReceiverList.java
final class ReceiverList extends ArrayList<BroadcastFilter>implements IBinder.DeathRecipient {final ActivityManagerService owner;public final IIntentReceiver receiver;public final ProcessRecord app;public final int pid;public final int uid;public final int userId;BroadcastRecord curBroadcast = null;boolean linkedToDeath = false;String stringName;ReceiverList(ActivityManagerService _owner, ProcessRecord _app,int _pid, int _uid, int _userId, IIntentReceiver _receiver) {owner = _owner;receiver = _receiver;app = _app;pid = _pid;uid = _uid;userId = _userId;}......public void binderDied() {linkedToDeath = false;owner.unregisterReceiver(receiver);}public boolean containsFilter(IntentFilter filter) {final int N = size();for (int i = 0; i < N; i++) {final BroadcastFilter f = get(i);if (IntentResolver.filterEquals(f, filter)) {return true;}}return false;}......
}
ReceiverList 继承于 ArrayList<BroadcastFilter>
,而 BroadcastFilter 又继承于 IntentFilter,所以 ReceiverList 可以被理解为一个 IntentFilter 数组列表。
final class BroadcastFilter extends IntentFilter {// Back-pointer to the list this filter is in.final ReceiverList receiverList;final String packageName;final String requiredPermission;final int owningUid;final int owningUserId;final boolean instantApp;final boolean visibleToInstantApp;BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,......) {super(_filter);receiverList = _receiverList;packageName = _packageName;requiredPermission = _requiredPermission;......}......
}
现在,我们可以绘制一张完整一点儿的图:
这张图只画了一个用户进程,在实际的系统里当然会有很多用户进程了,不过其关系是大致统一的,所以我们不再重复绘制。
关于动态 receiver 的注册,我们就先说这么多。至于激发广播时,又会做什么动作,我们会在后文阐述,现在我们先接着说明和动态 receiver 相对的静态 receiver。
三 静态BroadcastReceiver
静态 receiver 是指那些在 AndroidManifest.xml 文件中声明的 receiver,它们的信息会在系统启动时,由 Package Manager Service(PKMS)解析并记录下来。以后,当 AMS 调用 PKMS 的接口来查询 “和 intent 匹配的组件” 时,PKMS 内部就会去查询当初记录下来的数据,并把结果返回 AMS。
有的同学认为静态 receiver 是常驻内存的,这种说法并不准确。因为常驻内存的只是静态 receiver 的描述性信息,并不是 receiver 实体本身。
在 PKMS 内部,会有一个针对 receiver 而设置的 Resolver(决策器),其示意图如下:
需要说明的是以上的示意图是之前的 android 版本实现的,在 android 10.0 中,PKMS 中的 mActivities,mReceivers,mServices 等相关组件已经被封装到了 ComponentResolver 中了。
ComponentResolver.java
private final ActivityIntentResolver mActivities = new ActivityIntentResolver();private final ProviderIntentResolver mProviders = new ProviderIntentResolver();private final ActivityIntentResolver mReceivers = new ActivityIntentResolver();private final ServiceIntentResolver mServices = new ServiceIntentResolver();
3.1 ActivityIntentResolver
ComponentResolver.java # ActivityIntentResolver
private static final class ActivityIntentResolverextends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {@Overridepublic List<ResolveInfo> queryIntent(Intent intent, String resolvedType,boolean defaultOnly, int userId) {......}List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,int userId) {......}List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,int flags, List<PackageParser.Activity> packageActivities, int userId) {......}private void addActivity(PackageParser.Activity a, String type,List<PackageParser.ActivityIntentInfo> newIntents) {......}private void removeActivity(PackageParser.Activity a, String type) {mActivities.remove(a.getComponentName());......}......// Keys are String (activity class name), values are Activity.private final ArrayMap<ComponentName, PackageParser.Activity> mActivities =new ArrayMap<>();private int mFlags;
}
IntentResolver 是对 IntentFilter 操作的封装。
3.3 PackageParser.ActivityIntentInfo
PackageParser.java # ActivityIntentInfo
public final static class ActivityIntentInfo extends IntentInfo {@UnsupportedAppUsagepublic Activity activity;public ActivityIntentInfo(Activity _activity) {activity = _activity;}......public ActivityIntentInfo(Parcel in) {super(in);}
}
而 IntentInfo 又继承自 IntentFilter,代码如下:
PackageParser.java # IntentInfo
public static abstract class IntentInfo extends IntentFilter {......@UnsupportedAppUsageprotected IntentInfo() {}protected IntentInfo(Parcel dest) {super(dest);hasDefault = (dest.readInt() == 1);labelRes = dest.readInt();nonLocalizedLabel = dest.readCharSequence();icon = dest.readInt();logo = dest.readInt();banner = dest.readInt();preferred = dest.readInt();}......
}
3.4 ResolveInfo
ResolveInfo.java
public class ResolveInfo implements Parcelable {private static final String TAG = "ResolveInfo";public ActivityInfo activityInfo;public ServiceInfo serviceInfo;public ProviderInfo providerInfo;public IntentFilter filter;public String resolvePackageName;......public ResolveInfo(ResolveInfo orig) {activityInfo = orig.activityInfo;serviceInfo = orig.serviceInfo;providerInfo = orig.providerInfo;filter = orig.filter;priority = orig.priority;preferredOrder = orig.preferredOrder;match = orig.match;specificIndex = orig.specificIndex;labelRes = orig.labelRes;nonLocalizedLabel = orig.nonLocalizedLabel;icon = orig.icon;resolvePackageName = orig.resolvePackageName;noResourceId = orig.noResourceId;iconResourceId = orig.iconResourceId;system = orig.system;targetUserId = orig.targetUserId;handleAllWebDataURI = orig.handleAllWebDataURI;isInstantAppAvailable = orig.isInstantAppAvailable;instantAppAvailable = isInstantAppAvailable;}private ResolveInfo(Parcel source) {activityInfo = null;serviceInfo = null;providerInfo = null;switch (source.readInt()) {case 1:activityInfo = ActivityInfo.CREATOR.createFromParcel(source);break;case 2:serviceInfo = ServiceInfo.CREATOR.createFromParcel(source);break;case 3:providerInfo = ProviderInfo.CREATOR.createFromParcel(source);break;default:Slog.w(TAG, "Missing ComponentInfo!");break;}if (source.readInt() != 0) {filter = IntentFilter.CREATOR.createFromParcel(source);}priority = source.readInt();preferredOrder = source.readInt();match = source.readInt();specificIndex = source.readInt();labelRes = source.readInt();nonLocalizedLabel= TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);icon = source.readInt();resolvePackageName = source.readString();targetUserId = source.readInt();system = source.readInt() != 0;noResourceId = source.readInt() != 0;iconResourceId = source.readInt();handleAllWebDataURI = source.readInt() != 0;instantAppAvailable = isInstantAppAvailable = source.readInt() != 0;}
}
以上是涉及到到的各个核心数据结构,关于 PKMS 的查询动作的细节,可参考 PKMS 的相关文档。
目前我们只需知道,PKMS 向外界提供了 queryIntentReceivers(Intent intent, String resolvedType,…) 函数,该函数可以返回一个 ParceledListSlice<ResolveInfo> 列表。这个列表的所包含的信息为:有多少 receiver 对相关 Intent 的 Action 感兴趣。
总之就是,当系统发出一个广播时,PKMS 必须能够决策出,有多少静态 receiver 对这个广播感兴趣,而且这些 receiver 的信息分别又是什么。
关于 receiver 的注册动作,我们就先说这么多。下面我们来看看激发广播时的动作。
四 广播发送
时序图:
大家常见的激发广播的函数有哪些呢?从 ContextImpl.java 文件中,我们可以看到一系列发送广播的接口,列举如下:
- public void sendBroadcast(Intent intent)
- public void sendBroadcast(Intent intent, String receiverPermission)
- public void sendBroadcast(Intent intent, String receiverPermission, Bundle options)
- public void sendOrderedBroadcast(Intent intent, String receiverPermission)
- public void sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
- public void sendStickyBroadcast(Intent intent)
- public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
其中 sendBroadcast() 是最简单的发送广播的动作。而 sendOrderedBroadcast(),则是用来向系统发出有序广播 (Ordered broadcast) 的。这种有序广播对应的所有接收器只能按照一定的优先级顺序,依次接收 intent。这些优先级一般记录在 AndroidManifest.xml 文件中,具体位置在 <intent-filter> 元素的 android:priority 属性中,其数值越大表示优先级越高,取值范围为 -1000 到 1000。另外,有时候我们也可以调用 IntentFilter 对象的 setPriority() 方法来设置优先级。
对于有序广播而言,前面的接收者可以对接收到的广播 intent 进行处理,并将处理结果放置到广播 intent 中,然后传递给下一个接收者。需要注意的是,前面的接收者有权终止广播的进一步传播。也就是说,如果广播被前面的接收者终止了,那么后面的接收器就再也无法接收到广播了。
还有一个怪东西,叫做 sticky 广播,它又是什么呢?简单地说,sticky 广播可以保证 “在广播递送时尚未注册的 receiver”,一旦日后注册进系统,就能够马上接到 “错过” 的 sticky 广播。有关它的细节,我们在后文再说。
在发送方,我们熟悉的调用 sendBroadcast() 的代码片段如下:
4.1 ContextImpl.sendBroadcast
ContextImpl.java
public void sendBroadcast(Intent intent) {warnIfCallingFromSystemProcess();String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {intent.prepareToLeaveProcess(this);ActivityManager.getService().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType,null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,getUserId());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}
通过 Binder 机制,跨进程调用 AMS 的 broadcastIntent()。
4.2 AMS.broadcastIntent
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);final ProcessRecord callerApp = getRecordForAppLocked(caller);final int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();try {return broadcastIntentLocked(callerApp,callerApp != null ? callerApp.info.packageName : null,intent, resolvedType, resultTo, resultCode, resultData,resultExtras,requiredPermissions, appOp, bOptions, serialized, sticky,callingPid, callingUid, callingUid, callingPid, userId);} finally {Binder.restoreCallingIdentity(origId);}}
}
进而调用 broadcastIntentLocked。
4.3 AMS.broadcastIntentLocked
final int broadcastIntentLocked(ProcessRecord callerApp,String callerPackage, Intent intent, String resolvedType,IIntentReceiver resultTo, int resultCode, ......) {return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo, resultCode, resultData, resultExtras,requiredPermissions, appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,false /* allowBackgroundActivityStarts */);
}
继续调用 broadcastIntentLocked
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);......// 为 intent 添加 FLAG_EXCLUDE_STOPPED_PACKAGES 标记intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);......final boolean isProtectedBroadcast;try {isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);} catch (RemoteException e) {Slog.w(TAG, "Remote exception", e);return ActivityManager.BROADCAST_SUCCESS;}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) {......} else if (intent.getComponent() != null) {......} else {// Limit broadcast to their own package.intent.setPackage(callerPackage);}}}......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: if (checkComponentPermission(android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,callingPid, callingUid, -1, true)!= PackageManager.PERMISSION_GRANTED) {String msg = "Permission Denial: " + intent.getAction()+ " broadcast from " + callerPackage + " (pid=" + callingPid+ ", uid=" + callingUid + ")"+ " requires "+ android.Manifest.permission.BROADCAST_PACKAGE_REMOVED;Slog.w(TAG, msg);throw new SecurityException(msg);}switch (action) {case Intent.ACTION_UID_REMOVED:......case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:......break;case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:mAtmInternal.cleanupRecentTasksForUser(UserHandle.USER_ALL);break;case Intent.ACTION_PACKAGE_REMOVED:case Intent.ACTION_PACKAGE_CHANGED:......break;case Intent.ACTION_PACKAGES_SUSPENDED:case Intent.ACTION_PACKAGES_UNSUSPENDED:......break;}break;case Intent.ACTION_PACKAGE_REPLACED:{......break;}case Intent.ACTION_PACKAGE_ADDED:{......break;}case Intent.ACTION_PACKAGE_DATA_CLEARED:{......break;}case Intent.ACTION_TIMEZONE_CHANGED:mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);break;case Intent.ACTION_TIME_CHANGED:......break;case Intent.ACTION_CLEAR_DNS_CACHE:mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);break;case Proxy.PROXY_CHANGE_ACTION:mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG));break;case android.hardware.Camera.ACTION_NEW_PICTURE:case android.hardware.Camera.ACTION_NEW_VIDEO: intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);break;case android.security.KeyChain.ACTION_TRUST_STORE_CHANGED:mHandler.sendEmptyMessage(HANDLE_TRUST_STORAGE_UPDATE_MSG);break;case "com.android.launcher.action.INSTALL_SHORTCUT":Log.w(TAG, "Broadcast " + action+ " no longer supported. It will not be delivered.");return ActivityManager.BROADCAST_SUCCESS;case Intent.ACTION_PRE_BOOT_COMPLETED:timeoutExempt = true;break;}......}// Add to the sticky list if requested.if (sticky) {if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,callingPid, callingUid)!= PackageManager.PERMISSION_GRANTED) {String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="+ callingPid + ", uid=" + callingUid+ " requires " + android.Manifest.permission.BROADCAST_STICKY;Slog.w(TAG, msg);throw new SecurityException(msg);}if (requiredPermissions != null && requiredPermissions.length > 0) {Slog.w(TAG, "Can't broadcast sticky intent " + intent+ " and enforce permissions " + Arrays.toString(requiredPermissions));return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;}if (intent.getComponent() != null) {throw new SecurityException("Sticky broadcasts can't target a specific component");} if (userId != UserHandle.USER_ALL) {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++) {if (intent.filterEquals(list.get(i))) {throw new IllegalArgumentException("Sticky broadcast " + intent + " for user "+ userId + " conflicts with existing global broadcast");}}}}}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));}}......// 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 usersfor (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);}}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 (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;}// Merge into one list.int ir = 0;if (receivers != null) {......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++;}if (isCallerSystem) {checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,isProtectedBroadcast, receivers);}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) {Slog.w(TAG, "Failure ["+ queue.mQueueName + "] sending broadcast result of "+ intent, 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;}
broadcastIntentLocked() 是广播发送的核心函数,内容很多,函数很长,其中主要考虑的是以下方面的技术细节:
首先,有些广播 intent 只能由具有特定权限的进程发送,而有些广播 intent 在发送之前需要做一些其他动作。当然,如果发送方进程是系统进程、phone 进程、shell 进程等具有 root 权限的进程,那么必然有权发出广播。
另外,有时候用户希望发送 sticky 广播,以便日后注册的 receiver 可以收到 “错过” 的 sticky 广播。要达到这个目的,系统必须在内部维护一张 sticky 广播表,在具体的实现中,AMS 会把 sticky 类型的广播 intent 加入到 mStickyBroadcasts 映射表中。
mStickyBroadcasts 是一张哈希映射表,其 key 值为 intent 的 action 字符串,value 值为 “与这个 action 对应的 intent 数组列表” 的引用。当我们发送 sticky 广播时,新的广播 intent 要么替换掉 intent 数组列表中的某项,要么作为一个新项被添加进数组列表,以备日后使用。
发送广播时,还需要考虑所发送的广播是否需要有序(ordered)递送。而且,receiver 本身又分为动态注册和静态声明的,这让我们面对的情况更加复杂。从目前的代码来看,静态 receiver 一直是按照有序方式递送的,而动态 receiver 则需要根据 ordered 参数的值,做不同的处理。当我们需要有序递送时,AMS 会把动态 receivers 和静态 receivers 合并到一张表中,这样才能依照 receiver 的优先级,做出正确的处理,此时动态 receivers 和静态 receivers 可能呈现一种交错顺序。
另一方面,有些广播是需要发给特定目标组件的,这个也要加以考虑。
现在我们来分析 broadcastIntentLocked() 函数。我们可以将其逻辑大致整理成以下几步:
- 为 intent 添加 FLAG_EXCLUDE_STOPPED_PACKAGES 标记
- 判断进程是否有权限发出广播
- 处理和package相关的广播
- 处理其他一些系统广播
- 更新系统中的sticky列表
- 查询和 intent 匹配的静态 receivers
- 查询和 intent 匹配的动态 receivers
- 向并行 receivers 递送广播
- 整合(剩下的)并行 receivers,以及静态 receivers,形成一个串行 receivers 表
- 逐个向串行 receivers 递送广播
4.3.1 为intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标记
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
为什么 intent 要添加 FLAG_EXCLUDE_STOPPED_PACKAGES 标记呢?
原因是这样的,在 Android 3.1 之后,PKMS 加强了对 “处于停止状态的” 应用的管理。
- 如果一个应用在安装后从来没有启动过
- 或者这个应用已经被用户强制停止了
符合以上两个条件的应用,我们称这个应用处于停止状态(stopped state)。
为了达到精细调整的目的,Android 增加了2个 flag:FLAG_INCLUDE_STOPPED_PACKAGES 和 FLAG_EXCLUDE_STOPPED_PACKAGES,以此来表示 intent 是否要激活 “处于停止状态的” 应用。
从上面的 broadcastIntentLocked() 函数可以看到,在默认情况下,AMS 是不会把 intent 广播发给 “处于停止状态的” 应用的。
据说 Google 这样做是为了防止一些流氓软件或病毒干坏事。当然,如果广播的发起者认为自己的确需要广播到 “处于停止状态的” 应用的话,它可以让 intent 携带 INCLUDE_STOPPED_PACKAGES 标记,从这个标记的注释可以了解到,如果这两个标记同时设置的话,那么 INCLUDE 标记会 “取胜”,它会覆盖掉 framework 自动添加的 FLAG_EXCLUDE_STOPPED_PACKAGES 标记。
4.3.2 判断进程是否有权限发出广播
接着,broadcastIntentLocked() 会判断发送广播的进程是否有权限发出广播,代码截选如下:
// Verify that protected broadcasts are only being sent by system code,// and that system code is only sending protected broadcasts.final boolean isProtectedBroadcast; // 保护性广播try {isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);} catch (RemoteException e) {Slog.w(TAG, "Remote exception", e);return ActivityManager.BROADCAST_SUCCESS;}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.";......} else if (intent.getComponent() != null) {......} else {// Limit broadcast to their own package.intent.setPackage(callerPackage);}}}
如果发起方的 UID 为 SYSTEM_UID、PHONE_UID 或 BLUETOOTH_UID 等,或者发起方具有 root 权限,那么它一定有权限发送广播。
另外,还有一个 “保护性广播” 的概念,也要考虑进来。有些同学会询问 AndroidManifest.xml 中的一级标记 <protected-broadcast> 是什么意思。简单地说,Google 认为有一些广播是只能由系统发送,如果某个系统级 AndroidManifest.xml 中写了这个标记,那么在 PKMS 解析该文件时,就会把 “保护性广播” 标记中的名字(一般是 Action 字符串)记录下来。在系统运作起来之后,如果某个不具有系统权限的应用试图发送系统中的 “保护性广播”,那么到 AMS 的 broadcastIntentLocked() 处就会被拦住,AMS 会抛出异常,提示 “Permission Denial: not allowed to send broadcast”。
以下是 framework/base/core/res/AndroidManifest.xml 文件中部分保护性广播示例:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="android" coreApp="true" android:sharedUserId="android.uid.system"android:sharedUserLabel="@string/android_system_label"><!-- ================================================ --><!-- Special broadcasts that only the system can send --><!-- ================================================ --><eat-comment /><protected-broadcast android:name="android.intent.action.SCREEN_OFF" /><protected-broadcast android:name="android.intent.action.SCREEN_ON" /><protected-broadcast android:name="android.intent.action.USER_PRESENT" /><protected-broadcast android:name="android.intent.action.TIME_SET" /><protected-broadcast android:name="android.intent.action.TIME_TICK" /><protected-broadcast android:name="android.intent.action.TIMEZONE_CHANGED" />......
</manifest>
4.3.3 处理和package相关的广播
接下来需要处理一些系统级的 “Package 广播”,这些主要从 PKMS 处发来。
比如,当 PKMS 处理 APK 的添加、删除或改动时,一般会发出类似下面的广播:
- ACTION_PACKAGE_ADDED
- ACTION_PACKAGE_REMOVED
- ACTION_PACKAGE_CHANGED
- ACTION_PACKAGE_REPLACED 等广播。
AMS 必须确保发送 “包广播” 的发起方具有 BROADCAST_PACKAGE_REMOVED 权限,如果没有,那么 AMS 会抛出异常(SecurityException)。
接着,AMS 判断如果是某个用户 id 被删除了的话(Intent.ACTION_UID_REMOVED),那么必须把这件事通知给 “电池状态服务”。
另外,如果是 SD 卡等外部设备上的应用不可用了,这常常是因为卡被 unmount 了,此时 PKMS 会发出 Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE,而 AMS 则需要把 SD 卡上的所有包都强制停止,并立即发出另一个 “Package 广播”:EXTERNAL_STORAGE_UNAVAILABLE。
如果是某个外部包被删除或改动了(ACTION_PACKAGE_REMOVED / REPLACED),则要进一步判断 intent 里是否携带了 EXTRA_DONT_KILL_APP 额外数据,如果没有携带,说明需要立即强制结束 package,否则,不强制结束 package。看来有些应用即使在删除或改动了包后,还会在系统(内存)中保留下来并继续运行。另外,如果是删除包的话,此时要发出 PACKAGE_REMOVED 广播。
等等,以上只是简单列举了一些典型的包处理广播,还有很多针对其它的包广播的处理,再次不再赘述,同学们可以自行查看代码研究。
4.3.4 处理其他一些系统广播
broadcastIntentLocked() 不但要对 “Package 广播” 进行处理,还要关心其他一些系统广播。比如:
ACTION_TIMEZONE_CHANGED
ACTION_TIME_CHANGED
ACTION_CLEAR_DNS_CACHE
PROXY_CHANGE_ACTION
等等,感兴趣的同学可以自行研究这些广播的意义。
4.3.5 更新系统中的sticky列表
接着,broadcastIntentLocked() 中会判断当前发出的是否是 sticky 广播,如果是的话,必须把广播 intent 记录下来。
一开始会判断一下发起方是否具有发出 sticky 广播的权限,比如说要拥有 BROADCAST_STICKY 权限等等。判断合格后,broadcastIntentLocked() 会更新 AMS 里的一张表:mStickyBroadcasts,其处理代码如下:
// Add to the sticky list if requested.
if (sticky) {// 检查权限if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,callingPid, callingUid)!= PackageManager.PERMISSION_GRANTED) {String msg = "Permission Denial: broadcastIntent() requesting a" +" sticky broadcast from pid="+ callingPid + ", uid=" + callingUid+ " requires " + android.Manifest.permission.BROADCAST_STICKY;Slog.w(TAG, msg);throw new SecurityException(msg);}if (requiredPermissions != null && requiredPermissions.length > 0) {// sticky 广播不能 enforece permissionsSlog.w(TAG, "Can't broadcast sticky intent " + intent+ " and enforce permissions " + Arrays.toString(requiredPermissions));return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;}// sticky 广播不能指定特定的目标组件if (intent.getComponent() != null) {throw new SecurityException("Sticky broadcasts can't target a specific component");}// 如果用户 id 不是全局的, 也就是给特定用户发送的 sticky 广播// 检查这个广播是否和全局的 mStickyBroadcasts 列表中的广播有冲突 if (userId != UserHandle.USER_ALL) {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++) {if (intent.filterEquals(list.get(i))) {throw new IllegalArgumentException("Sticky broadcast " + intent + " for user "+ userId + " conflicts with existing global broadcast");}}}}}// 真正开始更新 mStickyBroadcasts 列表信息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) { // 表中没有对应的 action,则创建并添加list = new ArrayList<>();stickies.put(intent.getAction(), list);}final int stickiesCount = list.size();int i;// 遍历 action 对应 intent 列表,如果不存在则添加,如果存在则更新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));}
}
mStickyBroadcasts 的定义是这样的:
ActivityManagerService.java
final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =new SparseArray<ArrayMap<String, ArrayList<Intent>>>();
上面代码的 filterEquals() 函数会比较两个 intent 的 action、data、type、identifier、package、component 以及 categories 等信息,但不会比较 extra 数据。
如果两个 intent 的 action 是一样的,但其他信息不同,那么它们在 ArrayList<Intent> 中会被记成两个不同的 intent。而如果发现新发送的 intent 在 ArrayList 中已经有个 “相等的” 旧 intent 时,则会用新的替掉旧的。
以后,每当 AMS 收到注册新的动态 receiver 时,注册动作中都会遍历一下 mStickyBroadcast 表,看哪些 intent 可以和新 receiver 的 filter 匹配,只有匹配的 intent 才会递送给新 receiver,示意图如下:
图中通过动态注册的新 receiver 的 filter 只对 a1 和 a3 这两个 action 感兴趣,所以遍历时就不会考虑 mStickyBroadcast 表中的 a2 表项对应的子表,而 a1、a3 子表所对应的若干 intent 中又只有一部分可以和 filter 匹配,比如 a1 的 intent1 以及 a3 的 intent2,所以图中只选择了这两个 intent 递送给新 receiver。
除了更新 mStickyBoradcast 表的动作以外,sticky 广播和普通广播在 broadcastIntentLocked() 中的代码是一致的,并没有其他什么不同了。
4.3.6 向并行receivers递送广播
然后 broadcastIntentLocked() 会尝试向并行 receivers 递送广播。
此时会调用到 queue.scheduleBroadcastsLocked()。相关代码截选如下:
// Figure out who all will receive this broadcast.
// 先找出所有接收此广播的 receives 列表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) {......registeredReceivers = mReceiverResolver.queryIntent(intent,resolvedType, false /*defaultOnly*/, userId);......}......int NR = registeredReceivers != null ? registeredReceivers.size() : 0;if (!ordered && NR > 0) { // 不是有序的广播if (isCallerSystem) {checkBroadcastFromSystem(intent, callerApp, callerPackage,callingUid, isProtectedBroadcast, registeredReceivers);}// 获取广播队列 BroadcastQueue,可能是前台广播队列,也可能是后台广播队列final BroadcastQueue queue = broadcastQueueForIntent(intent);// 构建广播的数据结构 BroadcastRecordBroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, ......);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;}
简单地说,就是 new 一个 BroadcastRecord 节点,并插入 BroadcastQueue 内的并行处理队列,最后发起实际的广播调度:scheduleBroadcastsLocked()。关于上面代码中的 registeredReceivers 列表,我们会在后文说明,这里先跳过。
其实不光并行处理部分需要一个 BroadcastRecord 节点,串行处理部分也需要 BroadcastRecord 节点。也就是说,要发送一次广播,AMS 必须构造一个或两个 BroadcastRecord 节点,并将之插入合适的广播队列(mFgBroadcastQueue 或 mBgBroadcastQueue)。插入成功后,再执行队列的 scheduleBroadcastsLocked() 动作,进行实际的派发调度。示意图如下:
请注意图中 BroadcastRecord 节点所携带的节点链。
在 mParallelBroadcasts 表中,每个 BroadcastRecord 只可能携带 BroadcastFilter,因为平行处理的节点只会对应动态 receiver,而所有静态 receiver 只能是串行处理的
另一方面,在 mOrderedBroadcasts 表中,BroadcastRecord 中则既可能携带 BroadcastFilter,也可能携带 ResolveInfo。这个其实很容易理解,首先,ResolveInfo 对应静态 receiver,放到这里自不待言,其次,如果用户在发送广播时明确指定要按 ordered 方式发送的话,那么即使目标方的 receiver 是动态注册的,它对应的 BroadcastFilter 也会被强制放到这里
现在让我们再整合一下思路,做下小结:
- BroadcastRecord 节点内部的 receivers 列表,记录着和这个广播动作相关的目标 receiver 信息,该列表内部的子节点可能是 ResolveInfo 类型的,也可能是 BroadcastFilter 类型的。
- ResolveInfo 是从 PKMS 处查到的静态 receiver 的描述信息,它的源头是 PKMS 分析的那些 AndroidManifest.xml 文件。
而 BroadcastFilter,来自于一开始阐述动态 receiver 时,提到的 AMS 端的 mRegisteredReceivers 哈希映射表。现在,我们再画一张示意图:
因为 BroadcastRecord 里的 BroadcastFilter,和 AMS 的 mRegisteredReceivers 表中(间接)所指的对应 BroadcastFilter 是同一个对象,所以我是用虚线将它们连起来的。
Ok,我们接着看 scheduleBroadcastsLocked() 动作。scheduleBroadcastsLocked() 的代码如下:
BroadcastQueue.java
public void scheduleBroadcastsLocked() {if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["+ mQueueName + "]: current="+ mBroadcastsScheduled);if (mBroadcastsScheduled) {return;}mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));mBroadcastsScheduled = true;}
发出 BROADCAST_INTENT_MSG 消息。我们接下来看对这个消息的处理:
BroadcastQueue.java
private final class BroadcastHandler extends Handler {public BroadcastHandler(Looper looper) {super(looper, null, true);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case BROADCAST_INTENT_MSG: {if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["+ mQueueName + "]");processNextBroadcast(true);} break;case BROADCAST_TIMEOUT_MSG: {synchronized (mService) {broadcastTimeoutLocked(true);}} break;}}}
也就是说,AMS 端会在 BroadcastQueue.java 中的 processNextBroadcast() 具体处理广播。我们在下一篇文章中重点分析这个函数。
4.3.7 整理两个receiver列表
我们前文已经说过,有些广播是需要有序递送的。为了合理处理 “有序递送” 和 “平行递送”,broadcastIntentLocked() 函数内部搞出了两个 list:
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
其中,receivers 主要用于记录 “有序递送” 的 receiver,而 registeredReceivers 则用于记录与 intent 相匹配的动态注册的 receiver。
关于这两个 list 的大致运作是这样的:我们先利用包管理器的 queryIntentReceivers() 接口,查询出和 intent 匹配的所有静态 receivers,此时所返回的查询结果本身已经排好序了,因此,该返回值被直接赋值给了 receivers 变量,代码如下:
receivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
而对于动态注册的 receiver 信息,就不是从包管理器获取了,这些信息本来就记录在 AMS 之中,此时只需调用
registeredReceivers = mReceiverResolver.queryIntent(intent,resolvedType, false /*defaultOnly*/, userId);
就可以了。注意,此时返回的 registeredReceivers 中的子项是没有经过排序的。而关于 PKMS 的 queryIntentReceivers(),我们可以参考 PKMS 的专题文档,此处不再赘述。
- 如果是 “并行递送” 广播, registeredReceivers 中的各个 receiver 会在 scheduleBroadcastsLocked() 函数中被并行处理掉
- 大家回头看看向并行 receivers 发送广播的代码,会发现在调用完 scheduleBroadcastsLocked() 后,registeredReceivers 会被强制赋值成 null 值
- 如果是 “串行递送” 广播,那么必须考虑把 registeredReceivers 表合并到 receivers 表中去
- 我们知道,receivers 列表中只记录了一些静态 receiver,这些 receiver 将会被 “有序递送”。现在我们只需再遍历一下 registeredReceivers 列表,并将其中的每个子项插入到 receivers 列表的合适地方,就可以合并出一条顺序列表了。当然,如果 registeredReceivers 已经被设为 null 了,就无所谓合并了
为什么静态声明的 receiver 只会 “有序递送” 呢?我想也许和这种 receiver 的复杂性有关系,因为在需要递送广播时,receiver 所属的进程可能还没有启动呢,所以也许会涉及到启动进程的流程,这些都是比较复杂的流程。
当然,上面所说的是没有明确指定目标组件的情况,如果 intent 里含有明确的目标信息,那么就不需要调用包管理器的 queryIntentReceivers() 了,只需 new 一个 ArrayList,并赋值给 receivers,然后把目标组件对应的 ResolveInfo 信息添加进 receivers 数组列表即可。
4.3.8 逐个向串行receivers递送广播
当 receivers 列表整理完毕之后,现在要开始尝试逐个向串行 receivers 递送广播了。正如前文所说,这里要重新 new 一个新的 BroadcastRecord 节点:
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) {Slog.w(TAG, "Failure ["+ queue.mQueueName + "] sending broadcast result of "+ intent, e);}}} else {queue.enqueueOrderedBroadcastLocked(r);queue.scheduleBroadcastsLocked();}
}
而 scheduleBroadcastsLocked() 最终会间接导致走到 BroadcastQueue.java 中的 processNextBroadcast()。这一点和前文所说的 “向并行 receivers 递送广播” 的动作基本一致。
4.4 BroadcastQueue.processNextBroadcast
从 processNextBroadcast() 的代码,我们就可以看清楚前面说的 “平行广播”、“有序广播” 和 “动态 receiver”、“静态 receiver” 之间的关系了。
可以说:processNextBroadcast 函数是广播处理的核心。
我们在前文已经说过,所有的静态 receiver 都是串行处理的,而动态 receiver 则会按照发广播时指定的方式,进行 “并行” 或 “串行” 处理。
能够并行处理的广播,其对应的若干 receiver 一定都已经存在了,不会牵扯到启动新进程的操作,所以可以在一个 while 循环中,一次性全部 deliver。
而有序广播,则需要一个一个地处理,其循环处理的手段是发送事件,也就是说,在一个 receiver 处理完毕后,会利用广播队列(BroadcastQueue)的 mHandler,发送一个 BROADCAST_INTENT_MSG 事件,从而执行下一次的 processNextBroadcast()。
BroadcastQueue.java
final void processNextBroadcast(boolean fromMsg) {synchronized (mService) {processNextBroadcastLocked(fromMsg, false);}
}
继续调用 processNextBroadcastLocked,这个函数很长,我们先看下全部的代码,然后下面进行详细分析。
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {BroadcastRecord r;if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["+ mQueueName + "]: "+ mParallelBroadcasts.size() + " parallel broadcasts; "+ mDispatcher.describeStateLocked());mService.updateCpuStats();if (fromMsg) {mBroadcastsScheduled = false;}// First, deliver any non-serialized broadcasts right away.while (mParallelBroadcasts.size() > 0) {r = mParallelBroadcasts.remove(0);r.dispatchTime = SystemClock.uptimeMillis();r.dispatchClockTime = System.currentTimeMillis();......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);}// Now take care of the next serialized one...// If we are waiting for a process to come up to handle the next// broadcast, then do nothing at this point. Just in case, we// check that the process we're waiting for still exists.if (mPendingBroadcast != null) {if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,"processNextBroadcast [" + mQueueName + "]: waiting for "+ mPendingBroadcast.curApp);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 waitingreturn;} else {Slog.w(TAG, "pending app ["+ mQueueName + "]" + mPendingBroadcast.curApp+ " died before responding to broadcast");mPendingBroadcast.state = BroadcastRecord.IDLE;mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;mPendingBroadcast = null;}}boolean looped = false;do {final long now = SystemClock.uptimeMillis();r = mDispatcher.getNextBroadcastLocked(now);if (r == null) {// No more broadcasts are deliverable right now, so all done!mDispatcher.scheduleDeferralCheckLocked(false);mService.scheduleAppGcsLocked();if (looped) {// If we had finished the last ordered broadcast, then// make sure all processes have correct oom and sched// adjustments.mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);}// when we have no more ordered broadcast on this queue, stop loggingif (mService.mUserController.mBootCompleted &&mLogLatencyMetrics) {mLogLatencyMetrics = false;}return;}boolean forceReceive = false;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))) {Slog.w(TAG, "Hung broadcast ["+ mQueueName + "] discarded after timeout failure:"+ " now=" + now+ " dispatchTime=" + r.dispatchTime+ " startTime=" + r.receiverTime+ " intent=" + r.intent+ " numReceivers=" + numReceivers+ " nextReceiver=" + r.nextReceiver+ " state=" + r.state);broadcastTimeoutLocked(false); // forcibly finish this broadcastforceReceive = true;r.state = BroadcastRecord.IDLE;}}if (r.state != BroadcastRecord.IDLE) {if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,"processNextBroadcast("+ mQueueName + ") called when not idle (state="+ r.state + ")");return;}// Is the current broadcast is done for any reason?if (r.receivers == null || r.nextReceiver >= numReceivers|| r.resultAbort || forceReceive) {// Send the final result if requestedif (r.resultTo != null) {boolean sendResult = true;if (r.splitToken != 0) {int newCount = mSplitRefcounts.get(r.splitToken) - 1;if (newCount == 0) {mSplitRefcounts.delete(r.splitToken);} else {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);r.resultTo = null;} catch (RemoteException e) {r.resultTo = null;Slog.w(TAG, "Failure ["+ mQueueName + "] sending broadcast result of "+ r.intent, e);}}}cancelBroadcastTimeoutLocked();if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,"Finished with ordered broadcast " + r);// ... 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) {mService.addBroadcastStatLocked(r.intent.getAction(),r.callerPackage, r.manifestCount, r.manifestSkipCount,r.finishTime-r.dispatchTime);}mDispatcher.retireBroadcastLocked(r);r = null;looped = true;continue;}if (!r.deferred) {final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));if (mDispatcher.isDeferringLocked(receiverUid)) {BroadcastRecord defer;if (r.nextReceiver + 1 == numReceivers) {defer = r;mDispatcher.retireBroadcastLocked(r);} else {defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);if (DEBUG_BROADCAST_DEFERRAL) {Slog.i(TAG_BROADCAST, "Post split:");Slog.i(TAG_BROADCAST, "Original broadcast receivers:");for (int i = 0; i < r.receivers.size(); i++) {Slog.i(TAG_BROADCAST, " " + r.receivers.get(i));}Slog.i(TAG_BROADCAST, "Split receivers:");for (int i = 0; i < defer.receivers.size(); i++) {Slog.i(TAG_BROADCAST, " " + defer.receivers.get(i));}}// Track completion refcount as well if relevantif (r.resultTo != null) {int token = r.splitToken;if (token == 0) {r.splitToken = defer.splitToken = nextSplitTokenLocked();mSplitRefcounts.put(r.splitToken, 2);} else {final int curCount = mSplitRefcounts.get(token);mSplitRefcounts.put(token, curCount + 1);}}}mDispatcher.addDeferredBroadcast(receiverUid, defer);r = null;looped = true;continue;}}} while (r == null);// 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.r.receiverTime = SystemClock.uptimeMillis();if (recIdx == 0) {r.dispatchTime = r.receiverTime;r.dispatchClockTime = System.currentTimeMillis();if (mLogLatencyMetrics) {StatsLog.write(StatsLog.BROADCAST_DISPATCH_LATENCY_REPORTED,r.dispatchClockTime - r.enqueueClockTime);}if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),System.identityHashCode(r));Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),System.identityHashCode(r));}}if (! mPendingBroadcastTimeoutMessage) {long timeoutTime = r.receiverTime + mConstants.TIMEOUT;if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,"Submitting BROADCAST_TIMEOUT_MSG ["+ mQueueName + "] for " + r + " at " + timeoutTime);setBroadcastTimeoutLocked(timeoutTime);}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;if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,"Delivering ordered ["+ mQueueName + "] to registered "+ filter + ": " + r);deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);if (r.receiver == null || !r.ordered) {// The receiver has already finished, so schedule to// process the next one.if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["+ mQueueName + "]: ordered="+ r.ordered + " receiver=" + r.receiver);r.state = BroadcastRecord.IDLE;scheduleBroadcastsLocked();} else {if (filter.receiverList != null) {maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);}if (brOptions != null &&brOptions.getTemporaryAppWhitelistDuration() > 0) {scheduleTempWhitelistLocked(filter.owningUid,brOptions.getTemporaryAppWhitelistDuration(), r);}}return;}// 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;if (brOptions != null &&(info.activityInfo.applicationInfo.targetSdkVersion< brOptions.getMinManifestReceiverApiLevel() ||info.activityInfo.applicationInfo.targetSdkVersion> brOptions.getMaxManifestReceiverApiLevel())) {skip = true;}if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage,r.callingUid, component.getPackageName(),info.activityInfo.applicationInfo.uid)) {......skip = true;}if (!skip) {skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);if (skip) {......}}int perm = mService.checkComponentPermission(info.activityInfo.permission,r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,info.activityInfo.exported);if (!skip && perm != PackageManager.PERMISSION_GRANTED) {if (!info.activityInfo.exported) {......} else {......}skip = true;} else if (!skip && info.activityInfo.permission != null) {final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);if (opCode != AppOpsManager.OP_NONE&& mService.mAppOpsService.noteOperation(opCode, r.callingUid,r.callerPackage) != AppOpsManager.MODE_ALLOWED) {......skip = true;}}if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID && r.requiredPermissions != null &&r.requiredPermissions.length > 0) {for (int i = 0; i < r.requiredPermissions.length; i++) {String requiredPermission = r.requiredPermissions[i];try {perm = AppGlobals.getPackageManager().checkPermission(requiredPermission,info.activityInfo.applicationInfo.packageName,UserHandle.getUserId(info.activityInfo.applicationInfo.uid));} catch (RemoteException e) {perm = PackageManager.PERMISSION_DENIED;}if (perm != PackageManager.PERMISSION_GRANTED) {Slog.w(TAG, "Permission Denial: receiving "+ r.intent + " to "+ component.flattenToShortString()+ " requires " + requiredPermission+ " due to sender " + r.callerPackage+ " (uid " + r.callingUid + ")");skip = true;break;}int appOp = AppOpsManager.permissionToOpCode(requiredPermission);if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp&& mService.mAppOpsService.noteOperation(appOp,info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)!= AppOpsManager.MODE_ALLOWED) {Slog.w(TAG, "Appop Denial: receiving "+ r.intent + " to "+ component.flattenToShortString()+ " requires appop " + AppOpsManager.permissionToOp(requiredPermission)+ " due to sender " + r.callerPackage+ " (uid " + r.callingUid + ")");skip = true;break;}}}if (!skip && r.appOp != AppOpsManager.OP_NONE&& mService.mAppOpsService.noteOperation(r.appOp,info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)!= AppOpsManager.MODE_ALLOWED) {Slog.w(TAG, "Appop Denial: receiving "+ r.intent + " to "+ component.flattenToShortString()+ " requires appop " + AppOpsManager.opToName(r.appOp)+ " due to sender " + r.callerPackage+ " (uid " + r.callingUid + ")");skip = true;}boolean isSingleton = false;try {isSingleton = mService.isSingleton(info.activityInfo.processName,info.activityInfo.applicationInfo,info.activityInfo.name, info.activityInfo.flags);} catch (SecurityException e) {Slog.w(TAG, e.getMessage());skip = true;}if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {if (ActivityManager.checkUidPermission(android.Manifest.permission.INTERACT_ACROSS_USERS,info.activityInfo.applicationInfo.uid)!= PackageManager.PERMISSION_GRANTED) { skip = true;}}if (!skip && info.activityInfo.applicationInfo.isInstantApp()&& r.callingUid != info.activityInfo.applicationInfo.uid) {Slog.w(TAG, "Instant App Denial: receiving "+ r.intent+ " to " + component.flattenToShortString()+ " due to sender " + r.callerPackage+ " (uid " + r.callingUid + ")"+ " Instant Apps do not support manifest receivers");skip = true;}if (!skip && r.callerInstantApp&& (info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0&& r.callingUid != info.activityInfo.applicationInfo.uid) {Slog.w(TAG, "Instant App Denial: receiving "+ r.intent+ " to " + component.flattenToShortString()+ " requires receiver have visibleToInstantApps set"+ " due to sender " + r.callerPackage+ " (uid " + r.callingUid + ")");skip = true;}if (r.curApp != null && r.curApp.isCrashing()) {// If the target process is crashing, just skip it.Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r+ " to " + r.curApp + ": process crashing");skip = true;}if (!skip) {boolean isAvailable = false;try {isAvailable = AppGlobals.getPackageManager().isPackageAvailable(info.activityInfo.packageName,UserHandle.getUserId(info.activityInfo.applicationInfo.uid));} catch (Exception e) {// all such failures mean we skip this receiverSlog.w(TAG, "Exception getting recipient info for "+ info.activityInfo.packageName, e);}if (!isAvailable) {skip = true;}} if (!skip) {if (!requestStartTargetPermissionsReviewIfNeededLocked(r,info.activityInfo.packageName, UserHandle.getUserId(info.activityInfo.applicationInfo.uid))) {skip = true;}}// This is safe to do even if we are skipping the broadcast, and we need// this information now to evaluate whether it is going to be allowed to run.final int receiverUid = info.activityInfo.applicationInfo.uid;// If it's a singleton, it needs to be the same app or a special appif (r.callingUid != Process.SYSTEM_UID && isSingleton&& mService.isValidSingletonCall(r.callingUid, receiverUid)) {info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);}String targetProcess = info.activityInfo.processName;ProcessRecord app = mService.getProcessRecordLocked(targetProcess,info.activityInfo.applicationInfo.uid, false);if (!skip) {final int allowed = mService.getAppStartModeLocked(info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,info.activityInfo.applicationInfo.targetSdkVersion,-1, true, false, false);if (allowed != ActivityManager.APP_START_MODE_NORMAL) {// We won't allow this receiver to be launched if the app has been// completely disabled from launches, or it was not explicitly sent// to it and the app is in a state that should not receive it// (depending on how getAppStartModeLocked has determined that).if (allowed == ActivityManager.APP_START_MODE_DISABLED) {Slog.w(TAG, "Background execution disabled: receiving "+ r.intent + " to "+ component.flattenToShortString());skip = true;} else if (((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;}}}if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())&& !mService.mUserController.isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid), 0 /* flags */)) {skip = true;}if (skip) {if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,"Skipping delivery of ordered [" + mQueueName + "] "+ r + " for reason described above");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;if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "+ info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "+ receiverUid);}if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {scheduleTempWhitelistLocked(receiverUid,brOptions.getTemporaryAppWhitelistDuration(), r);}// 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) {Slog.w(TAG, "Failed trying to unstop package "+ r.curComponent.getPackageName() + ": " + 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) {Slog.w(TAG, "Exception when sending broadcast to "+ r.curComponent, e);} catch (RuntimeException e) {Slog.wtf(TAG, "Failed sending broadcast to "+ r.curComponent + " with " + r.intent, e);// If some unexpected exception happened, just skip// this broadcast. At this point we are not in the call// from a client, so throwing an exception out from here// will crash the entire system instead of just whoever// sent the broadcast.logBroadcastReceiverDiscardLocked(r);finishReceiverLocked(r, r.resultCode, r.resultData,r.resultExtras, r.resultAbort, false);scheduleBroadcastsLocked();// We need to reset the state if we failed to start the receiver.r.state = BroadcastRecord.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 (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,"Need to start app ["+ mQueueName + "] " + targetProcess + " for broadcast " + r);if ((r.curApp=mService.startProcessLocked(targetProcess,info.activityInfo.applicationInfo, true,r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,new HostingRecord("broadcast", r.curComponent),(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0,false, false)) == null) {// Ah, this recipient is unavailable. Finish it if necessary,// and mark the broadcast record as ready for the next.Slog.w(TAG, "Unable to launch app "+ info.activityInfo.applicationInfo.packageName + "/"+ receiverUid + " for broadcast "+ r.intent + ": process is bad");logBroadcastReceiverDiscardLocked(r);finishReceiverLocked(r, r.resultCode, r.resultData,r.resultExtras, r.resultAbort, false);scheduleBroadcastsLocked();r.state = BroadcastRecord.IDLE;return;}maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);mPendingBroadcast = r;mPendingBroadcastRecvIndex = recIdx;}
processNextBroadcastLocked() 的代码逻辑大体是这样的:先尝试处理 BroadcastQueue 中的 “平行广播” 部分。这需要遍历并行列表(mParallelBroadcasts)的每一个 BroadcastRecord 以及其中的 receivers 列表。对于平行广播而言,receivers 列表中的每个子节点是个 BroadcastFilter。我们直接将广播递送出去即可:
// First, deliver any non-serialized broadcasts right away.
// 遍历 mParallelBroadcasts 取出其中的每一个 BroadcastRecord
while (mParallelBroadcasts.size() > 0) {r = mParallelBroadcasts.remove(0);r.dispatchTime = SystemClock.uptimeMillis();r.dispatchClockTime = System.currentTimeMillis();......// 取出广播 BroadcastRecord 中对应的所有 receivers,然后发送广播final int N = r.receivers.size();for (int i=0; i<N; i++) {Object target = r.receivers.get(i);if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,"Delivering non-ordered on [" + mQueueName + "] to registered "+ target + ": " + r);// 发送广播deliverToRegisteredReceiverLocked(r,(BroadcastFilter)target, false, i);}addBroadcastToHistoryLocked(r);if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast [" + mQueueName + "] " + r);
}
4.4.1 deliverToRegisteredReceiverLocked()
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,BroadcastFilter filter, boolean ordered, int index) {boolean skip = false;if (!mService.validateAssociationAllowedLocked(r.callerPackage,r.callingUid, filter.packageName, filter.owningUid)) {skip = true;}if (!skip && !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid, r.callingPid, r.resolvedType,filter.receiverList.uid)) {skip = true;}......// 一堆判断条件,决定是否 skip,如果跳过了,则不会发送广播if (skip) {r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;return;}......// 到这里就开始执行广播的发送了r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;if (ordered) {// 此时发送的是并行广播,这个为 false,不会走此分支......}try {if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,"Delivering to " + filter + " : " + r);if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {......} else {r.receiverTime = SystemClock.uptimeMillis();maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);// 重点关注 filter.receiverList.receiver 参数,这个就是// IIntentReceiver,用来跨进程通信的performReceiveLocked(filter.receiverList.app,filter.receiverList.receiver,new Intent(r.intent), r.resultCode, r.resultData,r.resultExtras, r.ordered, r.initialSticky, r.userId);if (r.allowBackgroundActivityStarts && !r.ordered) {postActivityStartTokenRemoval(filter.receiverList.app, r);}}......} catch (RemoteException e) {......}
}
调用 performReceiveLocked 正式完成广播的发送。需要注意的是要想理解广播是怎么发送的,必须了解 BroadcastFilter 的数据结构组成,这个里边包含了 receiverList 这个变量,它里边的 app 是代表注册 BroadcastReceiver 应用的 ProcessRecord,而 receiverList 里边还有个变量 receiver 是最重要的,代表的是应用中注册的 BroadcastReceiver 对应的 ReceiverDispatcher 中的 IIntentReceiver 类型的 mIIntentReceiver,用来实现进程间的数据传输。
4.4.1.1 performReceiveLocked
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,Intent intent, int resultCode, String data, Bundle extras,boolean ordered, boolean sticky, int sendingUser)throws RemoteException {if (app != null) { // 进程存在if (app.thread != null) {try {// 完成 AMS 跨进程到客户端的调用app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras, ordered, sticky,sendingUser, app.getReportedProcState());} catch (RemoteException ex) {......}} else {// Application has died. Receiver doesn't exist.throw new RemoteException("app.thread must not be null");}} else {receiver.performReceive(intent, resultCode, data, extras, ordered,sticky, sendingUser);}}
4.4.1.2 ActivityThread#ApplicationThread.scheduleRegisteredReceiver
ActivityThread.java
public void scheduleRegisteredReceiver(IIntentReceiver receiver,Intent intent, int resultCode, String dataStr, Bundle extras,boolean ordered, boolean sticky, int sendingUser,int processState) throws RemoteException {updateProcessState(processState, false);// 广播发送到具体的 receiver 中receiver.performReceive(intent, resultCode, dataStr, extras, ordered,sticky, sendingUser);
}
最后调用到 LoadedApk.java 中的 ReceiverDispatcher 中
4.4.2 静态receiver的递送
说完动态递送,我们再来看静态递送。对于静态 receiver,情况会复杂很多,因为静态 receiver 所从属的进程有可能还没有运行起来呢。此时 BroadcastRecord 节点中记录的子列表的节点是 ResolveInfo 对象。
do {final long now = SystemClock.uptimeMillis();// 从 mDispatcher 中获取有序广播列表中的一个 BroadcastRecordr = mDispatcher.getNextBroadcastLocked(now);if (r == null) {......return; // 如果为空,则返回}......// 如果广播对应的 receiver 为空,或者其它条件成立,则跳过这次循环if (r.receivers == null || r.nextReceiver >= numReceivers|| r.resultAbort || forceReceive) {......r = null;continue;}
} while (r == null);// 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.r.receiverTime = SystemClock.uptimeMillis();......final Object nextReceiver = r.receivers.get(recIdx);......// 静态广播 receiver 是 ResolveInfo 形式的ResolveInfo info = (ResolveInfo)nextReceiver;ComponentName component = new ComponentName(info.activityInfo.applicationInfo.packageName,info.activityInfo.name);......// 获取目标 receiver 所在的进程信息String targetProcess = info.activityInfo.processName;ProcessRecord app = mService.getProcessRecordLocked(targetProcess,info.activityInfo.applicationInfo.uid, false);......if (skip) { // 如果skip了,则调用 scheduleBroadcastsLocked 执行下次消息循环......scheduleBroadcastsLocked();return;}......// 广播发送前的赋值操作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) {}......// Is this receiver's application already running?// 静态 receiver 所在进程是 runnting 状态if (app != null && app.thread != null && !app.killed) {try {app.addPackage(info.activityInfo.packageName,info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);maybeAddAllowBackgroundActivityStartsToken(app, r);// 关键点1: 静态 receiver 广播的发送processCurBroadcastLocked(r, app, skipOomAdj);return;} catch (RemoteException e) {Slog.w(TAG, "Exception when sending broadcast to "+ r.curComponent, e);} catch (RuntimeException e) {......return;}}// Not running -- get it started, to be executed when the app comes up.if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,"Need to start app ["+ mQueueName + "] " + targetProcess + " for broadcast " + r);// 关键点2:调用 mService.startProcessLocked 启动新进程if ((r.curApp=mService.startProcessLocked(targetProcess,info.activityInfo.applicationInfo, true,r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,new HostingRecord("broadcast", r.curComponent),(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) !=0, false, false)) == null) {......return;}maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);mPendingBroadcast = r;mPendingBroadcastRecvIndex = recIdx;
如果目标进程已经存在了,那么 app.thread 肯定不为 null,直接调用 processCurBroadcastLocked() 即可,否则就需要启动新进程了。启动的过程是异步的,可能很耗时,所以要把 BroadcastRecord 节点记入 mPendingBroadcast。
4.4.2.1 processCurBroadcastLocked()
private final void processCurBroadcastLocked(BroadcastRecord r,ProcessRecord app, boolean skipOomAdj) throws RemoteException {......r.receiver = app.thread.asBinder();r.curApp = app;app.curReceivers.add(r);app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);......// Tell the application to launch this receiver.r.intent.setComponent(r.curComponent);boolean started = false;try {......// 跨进程调用到 ActivityThread 中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 {......}
}
其中 ActivityInfo info 参数,记录着目标 receiver 的信息。
4.4.2.2 ApplicationThread.scheduleReceiver()
ActivityThread.java
public final void scheduleReceiver(Intent intent, ActivityInfo info,CompatibilityInfo compatInfo, int resultCode, String data,Bundle extras, boolean sync, int sendingUser, int processState) {updateProcessState(processState, false);// 把消息封装到 ReceiverData 中ReceiverData r = new ReceiverData(intent, resultCode, data, extras,sync, false, mAppThread.asBinder(), sendingUser);r.info = info;r.compatInfo = compatInfo;sendMessage(H.RECEIVER, r);
}
然后发送消息 H.RECEIVER,接收到此消息后,调用 handleReceiver((ReceiverData)msg.obj)
4.4.2.3 ActivityThread.handleReceiver
private void handleReceiver(ReceiverData data) {......String component = data.intent.getComponent().getClassName();LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);IActivityManager mgr = ActivityManager.getService();Application app;BroadcastReceiver receiver;ContextImpl context;try {app = packageInfo.makeApplication(false, mInstrumentation);context = (ContextImpl) app.getBaseContext();......// 通过反射机制生成 receiverjava.lang.ClassLoader cl = context.getClassLoader();data.intent.setExtrasClassLoader(cl);data.intent.prepareToEnterProcess();data.setExtrasClassLoader(cl);receiver = packageInfo.getAppFactory().instantiateReceiver(cl, data.info.name, data.intent);} catch (Exception e) {......}try {......sCurrentBroadcastIntent.set(data.intent);receiver.setPendingResult(data);// 回调 receiver 的 onReceive 函数receiver.onReceive(context.getReceiverRestrictedContext(),data.intent);} catch (Exception e) {......} finally {sCurrentBroadcastIntent.set(null);}if (receiver.getPendingResult() != null) {data.finish();}}
handleReceiver 中,会运用反射机制,创建出 BroadcastReceiver 对象,而后回调该对象的 onReceive() 成员函数。
4.4.2.4 必要时启动新进程
现在我们回过头来看,在目标进程尚未启动的情况下,是如何完成递送的。刚刚我们已经看到调用 mService.startProcessLocked 的句子了,只要不出问题,目标进程成功启动后就会调用 AMS 的 attachApplication()。
有关 attachApplication() 的详情,请参考 Android系统启动进程创建流程,此处我们只需知道它里面又会调用 attachApplicationLocked() 函数。
AMS.java
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {......// Check if a next-broadcast receiver is in this process...if (!badApp && isPendingBroadcastProcessLocked(pid)) {try {didSomething |= sendPendingBroadcastsLocked(app);......} catch (Exception e) {......}}......
}
它们的意思是,如果新启动的进程就是刚刚 mPendingBroadcast 所记录的进程的话,此时 AMS 就会执行 sendPendingBroadcastsLocked(app) 一句。
boolean sendPendingBroadcastsLocked(ProcessRecord app) {boolean didSomething = false;for (BroadcastQueue queue : mBroadcastQueues) {didSomething |= queue.sendPendingBroadcastsLocked(app);}return didSomething;
}
BroadcastQueue 的 sendPendingBroadcastsLocked() 函数如下:
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {boolean didSomething = false;final BroadcastRecord br = mPendingBroadcast;if (br != null && br.curApp.pid > 0 && br.curApp.pid == app.pid) {if (br.curApp != app) {Slog.e(TAG, "App mismatch when sending pending broadcast to "+ app.processName + ", intended target is " + + br.curApp.processName);return false;}try {mPendingBroadcast = null;// 发送广播,参考 #### 4.4.2.1processCurBroadcastLocked(br, app, false);didSomething = true;} catch (Exception e) {......}}return didSomething;
}
可以看到,既然目标进程已经成功启动了,那么 mPendingBroadcast 就可以赋值为 null 了。接着调用 sendPendingBroadcastsLocked() 和前文刚刚阐述的 processCurBroadcastLocked(),其内再通过 app.thread.scheduleReceiver(),将语义发送到用户进程,完成真正的广播递送。这部分在上一小节已有阐述,这里就不多说了。