php做视频网站有哪些软件下载,专业网站托管,wordpress4.7.4+for+sae,做网站一个月20万一 概述
广播 (Broadcast) 机制用于进程或线程间通信#xff0c;广播分为广播发送和广播接收两个过程#xff0c;其中广播接收者 BroadcastReceiver 是 Android 四大组件之一。BroadcastReceiver 分为两类#xff1a;
静态广播接收者#xff1a;通过 AndroidManifest.xm…一 概述
广播 (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 执行线程当 schedulernull 则默认代表在主线程中执行这也是最常见的用法。
使用过广播机制的同学对这个 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 过程
根据 BroadcastReceiver 获取对应的 ReceiverDispatcher
先根据 LoadedApk.getReceiverDispatcher从 ArrayMapBroadcastReceiver, ReceiverDispatcher 映射表中得到和 BroadcastReceiver 对应的 ReceiverDispatcher如果 LoadedApk 为空则新建 LoadedApk.ReceiverDispatcher
执行 ReceiverDispatcher.getIIntentReceiver() 得到 InnerReceiver (即 IIntentReceiver.Stub ) 调用 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 WeakReferenceLoadedApk.ReceiverDispatcher mDispatcher;final LoadedApk.ReceiverDispatcher mStrongRef;InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {mDispatcher new WeakReferenceLoadedApk.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 ArrayMapContext, ArrayMapBroadcastReceiver, ReceiverDispatcher mReceivers new ArrayMap();该表的 key 项是我们比较熟悉的 Context也就是说可以是 Activity、Service 或 Application。而 value 项则是另一张“子哈希表”。这是个 “表中表” 的形式。
言下之意就是每个 Context比如一个 activity是可以注册多个 receiver 的这个很好理解。mReceivers 里的“子哈希表”的 key 值为 BroadcastReceivervalue 项为 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;ArrayMapBroadcastReceiver, 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 ArrayMapBroadcastReceiver, 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 HashMapIBinder, 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 ArrayListBroadcastFilterimplements 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 继承于 ArrayListBroadcastFilter而 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 ServicePKMS解析并记录下来。以后当 AMS 调用 PKMS 的接口来查询 “和 intent 匹配的组件” 时PKMS 内部就会去查询当初记录下来的数据并把结果返回 AMS。
有的同学认为静态 receiver 是常驻内存的这种说法并不准确。因为常驻内存的只是静态 receiver 的描述性信息并不是 receiver 实体本身。 在 PKMS 内部会有一个针对 receiver 而设置的 Resolver决策器其示意图如下 需要说明的是以上的示意图是之前的 android 版本实现的在 android 10.0 中PKMS 中的 mActivitiesmReceiversmServices 等相关组件已经被封装到了 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 IntentResolverPackageParser.ActivityIntentInfo, ResolveInfo {Overridepublic ListResolveInfo queryIntent(Intent intent, String resolvedType,boolean defaultOnly, int userId) {......}ListResolveInfo queryIntent(Intent intent, String resolvedType, int flags,int userId) {......}ListResolveInfo queryIntentForPackage(Intent intent, String resolvedType,int flags, ListPackageParser.Activity packageActivities, int userId) {......}private void addActivity(PackageParser.Activity a, String type,ListPackageParser.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 ArrayMapComponentName, 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,…) 函数该函数可以返回一个 ParceledListSliceResolveInfo 列表。这个列表的所包含的信息为有多少 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, Cant 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 cant target a specific component);} if (userId ! UserHandle.USER_ALL) {ArrayMapString, ArrayListIntent stickies mStickyBroadcasts.get(UserHandle.USER_ALL);if (stickies ! null) {ArrayListIntent list stickies.get(intent.getAction());if (list ! null) {int N list.size();int i;for (i0; iN; i) {if (intent.filterEquals(list.get(i))) {throw new IllegalArgumentException(Sticky broadcast intent for user userId conflicts with existing global broadcast);}}}}}ArrayMapString, ArrayListIntent stickies mStickyBroadcasts.get(userId);if (stickies null) {stickies new ArrayMap();mStickyBroadcasts.put(userId, stickies);}ArrayListIntent 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;ListBroadcastFilter 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;}ListBroadcastFilter 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... lets 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个 flagFLAG_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:androidhttp://schemas.android.com/apk/res/androidpackageandroid coreApptrue android:sharedUserIdandroid.uid.systemandroid:sharedUserLabelstring/android_system_label!-- --!-- Special broadcasts that only the system can send --!-- --eat-comment /protected-broadcast android:nameandroid.intent.action.SCREEN_OFF /protected-broadcast android:nameandroid.intent.action.SCREEN_ON /protected-broadcast android:nameandroid.intent.action.USER_PRESENT /protected-broadcast android:nameandroid.intent.action.TIME_SET /protected-broadcast android:nameandroid.intent.action.TIME_TICK /protected-broadcast android:nameandroid.intent.action.TIMEZONE_CHANGED /......
/manifest4.3.3 处理和package相关的广播
接下来需要处理一些系统级的 “Package 广播”这些主要从 PKMS 处发来。
比如当 PKMS 处理 APK 的添加、删除或改动时一般会发出类似下面的广播
ACTION_PACKAGE_ADDEDACTION_PACKAGE_REMOVEDACTION_PACKAGE_CHANGEDACTION_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, Cant 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 cant target a specific component);}// 如果用户 id 不是全局的 也就是给特定用户发送的 sticky 广播// 检查这个广播是否和全局的 mStickyBroadcasts 列表中的广播有冲突 if (userId ! UserHandle.USER_ALL) {ArrayMapString, ArrayListIntent stickies mStickyBroadcasts.get(UserHandle.USER_ALL);if (stickies ! null) {ArrayListIntent list stickies.get(intent.getAction());if (list ! null) {int N list.size();int i;for (i0; iN; i) {if (intent.filterEquals(list.get(i))) {throw new IllegalArgumentException(Sticky broadcast intent for user userId conflicts with existing global broadcast);}}}}}// 真正开始更新 mStickyBroadcasts 列表信息ArrayMapString, ArrayListIntent stickies mStickyBroadcasts.get(userId);if (stickies null) { // 空表的话就创建一个新的stickies new ArrayMap();mStickyBroadcasts.put(userId, stickies);}ArrayListIntent 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 SparseArrayArrayMapString, ArrayListIntent mStickyBroadcasts new SparseArrayArrayMapString, ArrayListIntent();上面代码的 filterEquals() 函数会比较两个 intent 的 action、data、type、identifier、package、component 以及 categories 等信息但不会比较 extra 数据。
如果两个 intent 的 action 是一样的但其他信息不同那么它们在 ArrayListIntent 中会被记成两个不同的 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;ListBroadcastFilter 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;
ListBroadcastFilter 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 i0; iN; 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 were 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) {// Its 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.flagsActivityInfo.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 its 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 wont 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 cant 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 receivers 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.curAppmService.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 i0; iN; 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 doesnt 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.javapublic 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 cant be stopped.try {AppGlobals.getPackageManager().setPackageStoppedState(r.curComponent.getPackageName(), false, r.userId);} catch (RemoteException e) {}......// Is this receivers 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.curAppmService.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.javapublic 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()将语义发送到用户进程完成真正的广播递送。这部分在上一小节已有阐述这里就不多说了。