前言:知其所以,知其所以然。
广播使用的三步骤:
- 注册广播
- 发送广播
- 接收广播
本文主要谈一下《发送广播、接收广播》,广播光注册了还没用,只是告诉大家有这么个广播,但是没有发送,不起作用,因为当前的广播没有被触发,广播接收器也接收不到任何信息。
发送广播的初始触发函数是Context --> sendBroadcast(...),下面有几个重载:
public abstract void sendBroadcast(@RequiresPermission Intent intent);
public abstract void sendBroadcast(@RequiresPermission Intent intent,
@Nullable String receiverPermission);
public abstract void sendBroadcast(Intent intent,
@Nullable String receiverPermission,
@Nullable Bundle options);
//这个开发者一般是不允许调用的
public abstract void sendBroadcast(Intent intent,
String receiverPermission, int appOp);
我们从最基本的sendBroadcast谈起,谈谈发送广播的流程。
上面是发送广播的时序图,我们会根据这张图,一点点的分析发送广播的流程。
1.ContextImpl -> sendBroadcast(Intent intent)
ActivityManager.getService().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
这儿直接执行binder ipc调用,调用到AMS中,具体的细节在上一章《Android广播剖析之广播注册》已经介绍过。在这里我们介绍一下各个参数的含义:
找到函数定义的地方:
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)
函数参数 | 含义 |
---|---|
caller | IApplicationThread binder对象,是ActivityThread中持有的对象,通过这个对象可以在Service端找到调用的进程 |
intent | 传入的意图对象,包含广播的action和一些简单的值 |
resolvedType | Context都会持有一个ApplicationContentResolver对象,这个类继承ContentResolver,如果传入的intent中包含对ContentResolver的处理,可以通过这个获取对应的resolvedType,在Service接收的时候需要这个resolvedType来处理相应的权限问题 |
resultTo | IIntentReceiver binder对象,在上一章注册广播的时候讲过,一般传入的参数是空 |
resultCode | 默认传入Activity.RESULT_OK |
resultData | 返回的结果数据,传入是空 |
resultExtras | 返回的结果额外数据,传入是空 |
requiredPermissions | 需要的权限,这个针对一些需要特殊权限的广播 |
appOp | 是否需要权限认证 |
bOptions | |
serialized | |
sticky | 是否是粘性广播 |
userId | 用户的id |
这儿特别提一下:粘性广播这个概念已经在api-21被废弃了,因为google觉得
它们不提供安全性(任何人都可以访问它们),没有保护(任何人都可以修改它们)以及许多其他问题。建议的模式是使用非粘性广播来报告某些内容已更改,并使用另一种机制让应用程序在需要时检索当前值。可见安全性是google越来越重视的。
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();
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, appOp, bOptions, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
这是AMS调用的第一个函数,函数中执行的主要工作是:
- 认证当前的广播是否合法
- 根据传入的caller在service获取当前的调用进程
- 将uid和pid设置好,继续调用下一步函数
2.1 认证当前的广播是否合法
intent = verifyBroadcastLocked(intent);
这里的合法性校验主要做三件事情:
- 当intent中有文件泄漏风险的话,当前的广播不合法。
- 如果当前的系统还没有准备好,当前的广播不合法。
- 如果当前的intent flag中包含Intent.FLAG_RECEIVER_BOOT_UPGRADE,说明当前系统正在升级中,当前的广播不合法。
2.2 获取当前的调用进程
final ProcessRecord callerApp = getRecordForAppLocked(caller);
这个函数中主要从mLruProcesses变量中取相应的ProcessRecord,mLruProcesses使用最近最少使用的算法存储ProcessRecord列表,它构建的地方是attachApplicationLocked(...),每次check到应用的application的时候,就会执行这个方法,就是新启动一个新的进程。执行到这儿,就要更新mLruProcesses中的ProcessRecord,在讲解Activity章节的时候,会详细分析一下这个方法的。
final ArrayList mLruProcesses = new ArrayList();
获取到的ProcessRecord,包含很多进程相关的属性,有兴趣参考一下:
frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
2.3 设置uid与pid
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
他们直接调用的是Binder中的静态方法:
public static final native int getCallingPid();
public static final native int getCallingUid();
这是两个静态的native方法,getCallingPid() 是当前操作处理的进程号,这个方法是可以跨进程使用的,可以用来检验当前系统服务的身份或者校验权限是否正常。
getCallingPid()返回当前操作处理进程的Linux uid,因为底层使用的是linux kernel,所以使用的native方法也会调用linux kernel uid的那一套逻辑。这儿的uid和pid一样,也是起到校验的作用。
3.AMS->broadcastIntentLocked
这个方法相当重磅,是广播处理的核心方法,一系列重要的逻辑都在这里面处理。我们本小节的目的就是要分解这一个个方法,深入广播处理的逻辑里面,剖析一些整体的处理流程。
3.1 发送广播到一个禁止的user
这种情况下是广播发生是失败的,返回值是:
return ActivityManager.BROADCAST_FAILED_USER_STOPPED
if (userId != UserHandle.USER_ALL && !mUserController.isUserOrItsParentRunning(userId)) {
if ((callingUid != SYSTEM_UID
|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
Slog.w(TAG, "Skipping broadcast of " + intent
+ ": user " + userId + " and its parent (if any) are stopped");
return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
}
}
导致失败的情况是:
- step1:当前的userId不是接收广播的用户,并且当前的用户的parent载体正不在运行中。
- step2:如果step1不成立,直接跳过,在本步骤不会返回广播失败。
- step3:如果step1成立,那么继续判断step4
- step4:如果当前不是SYSTEM_UID或者当前的intent flag不是FLAG_RECEIVER_BOOT_UPGRADE的话,并且当前的intent action不等于Intent.ACTION_SHUTDOWN的话,直接返回BROADCAST_FAILED_USER_STOPPED,说明当前的广播失败因为用户被禁止了。
Note:
- Intent.FLAG_RECEIVER_BOOT_UPGRADE
这个广播意图代表boot更新,特殊模型下是可以等系统准备好之后再发送这个广播的。上面步骤中,step1这个大条件就排除了很多情况。- Intent.ACTION_SHUTDOWN="android.intent.action.ACTION_SHUTDOWN"
这个intent action是匹配设备关机的action
3.2 检查一下当前的broadcastOptions
这个BroadcastOptions中有四中只要的变量:
- mTemporaryAppWhitelistDuration
static final String KEY_TEMPORARY_APP_WHITELIST_DURATION
= "android:broadcast.temporaryAppWhitelistDuration";
- mMinManifestReceiverApiLevel
static final String KEY_MIN_MANIFEST_RECEIVER_API_LEVEL
= "android:broadcast.minManifestReceiverApiLevel";
- mMaxManifestReceiverApiLevel
static final String KEY_MAX_MANIFEST_RECEIVER_API_LEVEL
= "android:broadcast.maxManifestReceiverApiLevel";
- mDontSendToRestrictedApps
不允许发送广播到限制的应用。
static final String KEY_DONT_SEND_TO_RESTRICTED_APPS =
"android:broadcast.dontSendToRestrictedApps";
这四个变量分别代表broadcast中重要参数,可以控制当前的广播是否有效等等。它是通过Bundle传递过来的,Bundle中放入这几个参数变量,然后在AMS中转化成BroadcastOptions对象。如果符合broadcastOptions对象中参数设置的要求,当前的广播才合法,否则不合法。
3.3 是否系统调用者的权限检验
这儿的代码用了两个变量:
- isCallerSystem
调用者是否来自系统- isProtectedBroadcast
是否是受保护的广播
3.3.1 isCallerSystem赋值
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:
isCallerSystem = true;
break;
default:
isCallerSystem = (callerApp != null) && callerApp.persistent;
break;
}
强调最后一句:(callerApp != null) && callerApp.persistent 当前的调用进程中含有android:persistent = "true"的也是来自系统的,系统app都有这个标识的。
3.3.2 isProtectedBroadcast赋值
final boolean isProtectedBroadcast;
try {
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception", e);
return ActivityManager.BROADCAST_SUCCESS;
}
根据当前的action判断此broadcast是否是受保护的广播。
AppGlobals.getPackageManager().isProtectedBroadcast(action),直接调用到PackageManagerService.java中。
public boolean isProtectedBroadcast(String actionName) {
// allow instant applications
synchronized (mProtectedBroadcasts) {
if (mProtectedBroadcasts.contains(actionName)) {
return true;
} else if (actionName != null) {
// TODO: remove these terrible hacks
if (actionName.startsWith("android.net.netmon.lingerExpired")
|| actionName.startsWith("com.android.server.sip.SipWakeupTimer")
|| actionName.startsWith("com.android.internal.telephony.data-reconnect")
|| actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) {
return true;
}
}
}
- 当前PMS中维护了一个ArraySet
变量,会在解析AndroidManifest.xml的时候将符合条件的action放入集合列表中。 - 过滤特定的actionName,返回true
3.3.3 通过isCallerSystem与isProtectedBroadcast判断当前的广播是否合法
if (!isCallerSystem) {
if (isProtectedBroadcast) {
//step1
} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
if (callerPackage == null) {
//step2
} else if (intent.getComponent() != null) {
if (!intent.getComponent().getPackageName().equals(
callerPackage)) {
//step3
}
} else {
intent.setPackage(callerPackage);
}
}
}
当前判断的大前提是调用者并非来自系统。
- step1:如果是受保护的广播,直接抛出异常,非系统调用者无法操作受保护的广播
- step2:如果当前调用者为空, 直接抛出异常。
- step3:component中的pkgName和调用者中的pkgName对比,双重校验。
3.4 action判断
这儿一长串的代码,将近300行,都是对action的判断,下面会分解这部分的执行步骤,将核心的关键点拎出来。
3.4.1 特殊Intent action处理
这里列出的几个特殊的Intent action:这些intent action的共同点都是只能被系统所使用,普通的应用开发者无权使用。
- Intent.ACTION_UID_REMOVED
user Id被从系统中去掉。- Intent.ACTION_PACKAGE_REMOVED
一个存在的应用程序包被从设备中去掉。- Intent.ACTION_PACKAGE_CHANGED
一个存在的应用程序包被改变。- Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE
外部的应用程序无法访问。- Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
外部的应用程序可以访问。- Intent.ACTION_PACKAGES_SUSPENDED
应用程序被挂起。- Intent.ACTION_PACKAGES_UNSUSPENDED
应用程序被取消挂起。
在这些特殊的intent action条件下,需要判断一下当前的uid和pid是否有android.Manifest.permission.BROADCAST_PACKAGE_REMOVED权限,如果没有这个权限,直接抛出异常,因为没有这个权限无法操作这些intent action
下面重点谈一谈其中4个重要的action:
- Intent.ACTION_PACKAGE_REMOVED 与 Intent.ACTION_PACKAGE_CHANGED
当前intent中包含的getData()如果不存在则不作其他的操作,如果存在,则取出其中的特殊scheme的值,这个值是设置此action的对接字符串。当前如果是removed的话,需要清楚最近任务、电池服务、权限管理服务的中关闭这个进程的相关数据。
如果不是removed,是changed,也要杀掉当前的进程,并且清楚原有进程的进程数据,给新进程分配相应值。- Intent.ACTION_PACKAGES_SUSPENDED 与 Intent.ACTION_PACKAGES_UNSUSPENDED
进程的状态发生suspend与unsuspend变化,这会影响最近任务的任务栈,需要更新一下最近任务的服务。
3.4.2 其他intent action处理
这里列出其他的intent action,不作详细的描述了,大家了解即可,都是一些和系统参数相关的action,接收到这种action之后,需要操作一些相关的系统服务。
intent action | 描述 |
---|---|
Intent.ACTION_PACKAGE_REPLACED | 新版本的程序包替换旧版本 |
Intent.ACTION_PACKAGE_ADDED | 应用程序安装的action |
Intent.ACTION_PACKAGE_DATA_CLEARED | 程序数据被清空 |
Intent.ACTION_TIMEZONE_CHANGED | 时区改变的广播 |
Intent.ACTION_TIME_CHANGED | 时间改变的广播 |
Intent.ACTION_CLEAR_DNS_CACHE | DNS缓存清空的广播 |
Proxy.PROXY_CHANGE_ACTION | 代理改变的广播 |
android.hardware.Camera.ACTION_NEW_PICTURE | 拍照广播 |
android.hardware.Camera.ACTION_NEW_VIDEO | 拍视频广播 |
3.5 粘性广播的处理
粘性广播已经在新版本上被废弃了,主要这种广播会产生很多安全性和权限的问题,这也可以看出来Android的演变方式,越来越注重隐私和权限了。
3.6 处理广播
上面只是在处理广播之前,我们要确保当前的额广播是符合权限条件的,是合法的(uid与pid的校验合法)等等,等这些验证条件满足之后我们会处理之前注册的广播了。
我们都知道广播有两种形式:
- 静态注册广播
- 动态注册广播
那么这两种广播类型,谁的优先级比较高了,代码中已经揭秘了,动态注册的广播优先级比较高。我们按照执行的顺序往下捋。
List receivers = null;
List registeredReceivers = null;
上面两个重要的变量,其中receivers是处理静态注册的广播的receivers的结构。registeredReceivers是动态注册广播的receiver集合。
3.6.1 动态注册广播处理
获取动态注册广播的代码:
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
for (int i = 0; i < users.length; i++) {
if (mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
continue;
}
List 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);
}
}
- 当调用的uid是SHELL_UID的时候,将所有用户注册的广播都取出来。
- 调用的uid是其他的,直接取广播就可以的。
动态注册的广播是在代码中注册,注册的广播会存储在AMS中,取广播的的操作在:
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, userId);
将注册的动态广播取出来,根据action和category等匹配属性。
取出来registeredReceivers之后,接下来开始处理工作。
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
resultCode, resultData, resultExtras, ordered, sticky, false, userId);
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;
}
- 如果是来自系统方面的调用,需要check一下当前的广播是否受保护,也要检查一下当前的action是否符合条件。
- 取一个存在的BroadcastQueue,里面按照队列的方式放入BroadcastRecord
- 构建一个BroadcastRecord对象,将进程属性和broadcast属性和注册的registeredReceivers传入BroadcastRecord中
- 如果当前的应用程序没有被替换,就执行两个调用。
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
- queue.enqueueParallelBroadcastLocked(r)
当前的BroadcastRecord ,存入mParallelBroadcasts 列表中。- queue.scheduleBroadcastsLocked()
开始调度广播。调度到BroadcastQueue->scheduleBroadcastsLocked()中,发送一个BROADCAST_INTENT_MSG到此类中的handler中。handleMessage中处理的地方是:
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
}
} break;
}
}
最终调度的方法是processNextBroadcastLocked(......),这个方法是处理广播调度的核心方法,我们需要好好分析一下这个方法的执行动作。
下面用一个流程图表达一下此方法执行的过程。放在《4.processNextBroadcastLocked方法剖析》中讲解这个调用。
3.6.2 静态注册广播处理
获取静态注册广播的代码:
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
静态注册的广播是在AndroidManifest.xml中注册的receiver,其中必定涉及到PMS中的解析操作,将AndroidManifest.xml中的广播解析出来。
到PMS中解析AndroidManifest.xml的操作是:
List newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
这儿大家了解即可,在讲解PMS章节的时候,我会详细分析的。
4.processNextBroadcastLocked方法剖析
无论是动态注册的广播还是静态注册的广播,最终处理的地方都会流转到这儿,所以说这里是处理广播的核心函数。讲清楚这个函数的流程,将有助于我们搞清楚广播的处理的核心要点。
这里我将广播处理的整体流程分为3个重要的模块:
- 前置条件处理模块
- 权限校验处理模块
- 广播发送和接收的核心处理模块
4.1 前置条件处理模块
- 更新cpu状态
将当前处理时间更新到本地的cpu状态上。- 处理并行广播
平时发送的广播一般都是并行的,如果没有调用发生有序广播,都会发生并行广播的这个变量集合中,这儿处理的就是这个并行广播的集合。
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index)
调用这个函数,其中传入的ordered变量为false,代表此时处理的是并行广播。这个函数主要是校验权限,核心的处理是针对有序的。下面将有序的时候再讲一下。
然后执行:
addBroadcastToHistoryLocked(r);
将当前的BroadcastQueue放入历史列表中。接下来 处理的时候会用到。
- 处理即将要处理的广播
何为pendingBroadcast,就是针对那些本来即将处理,但是因为receiver所在的进程没有启动,导致当前的broadcast暂时无法处理,所以标记为pendingBroadcast。- 处理有序广播
这里的有序广播就是我们发送的时候使用public void sendOrderedBroadcast(Intent intent, String receiverPermission)或者其重载函数发送的有序广播,因为是有序广播,所以要因此处理,那就会存在超时的情况。
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
这个mTimeoutPeriod是构造的时候传入的参数:
BroadcastQueue(ActivityManagerService service, Handler handler,
String name, long timeoutPeriod, boolean allowDelayBehindServices) {
mService = service;
mHandler = new BroadcastHandler(handler.getLooper());
mQueueName = name;
mTimeoutPeriod = timeoutPeriod;
mDelayBehindServices = allowDelayBehindServices;
}
当处理broadcast处理时间超过 (2mTimeoutPeriodnumReceivers),此时广播超时,开始进入超时广播的处理流程。执行超时广播的函数是:
broadcastTimeoutLocked(false);
- 处理超时广播
广播超时的时候执行的函数是broadcastTimeoutLocked(boolean fromMsg),其中的参数fromMsg表示当前的函数执行是否是通过Handler->sendMessage来调用的,一个是抛出到另一个线程中执行,一个就在当前线程中执行。这也要分情况讨论。
当fromMsg为true的时候:
(1)如果当前系统没有准备好,则直接退出。程序中多次使用mService.mProcessesReady来标识程序是否准备好,这是一个volatile类型变量,在AMS->systemReady(...)中赋值。
(2)设置广播超时时间再继续处理:执行setBroadcastTimeoutLocked(long timeoutTime),timeoutTime时间之后再处理这个广播。
下面的执行与fromMsg无关:
(3)当前的广播已经结束,直接返回
(4)结束当前广播的receiver,并从receivers中移除。
(5)发送一个anr发生的消息,告知当前的广播导致anr了。
if (!debugging && anrMessage != null) {
// Post the ANR to the handler since we do not want to process ANRs while
// potentially holding our lock.
mHandler.post(new AppNotResponding(app, anrMessage));
}
4.2 权限校验处理模块
这儿一长串代码,有几个权限判断的关键变量:
- ResolveInfo info = (ResolveInfo)nextReceiver;
- ComponentName component = new ComponentName(info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);- boolean skip = false;
- int perm = mService.checkComponentPermission(info.activityInfo.permission,
r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
info.activityInfo.exported);
这个判断当前广播权限是否满足的变量。
这儿的nextReceiver是从BroadcastRecord中receivers变量中取出来的,当nextReceiver是BroadcatsFilter对象的时候,前面说过,这时候当前广播的Component为null,说明此时广播没有接收者,是一个空闲广播。反之如果nextReceiver是ResolveInfo对象的时候,说明广播的Component不为null,可以接着往下处理。
==============================================================
上面的skip变量是权限模块贯彻到底的判断变量,会在每个权限判断中赋值,当前权限不通过,skip=true,那就不需要继续向下判断权限了。给大家看一下代码小例子。
if (!skip && r.appOp != AppOpsManager.OP_NONE
&& mService.mAppOpsService.noteOperation(r.appOp,
info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
!= AppOpsManager.MODE_ALLOWED) {
skip = true;
}
if (!skip) {
skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
}
可以看出来,如果上面skip=true,接下来直接跳出所有的权限判断,直接到最下面一步。
if (skip) {
r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
r.manifestSkipCount++;
scheduleBroadcastsLocked();
return;
}
直接return出去。可以看出这些权限是有优先级的,优先级高的权限放在上面判断处理,优先级低的权限放在下面处理。
4.3 广播发送和接收的核心处理模块
满足了广播处理的权限判断,接下来开始进入广播分发处理的流程。
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;
广播状态置为APP_RECEIVE,表明即将接收广播。
4.3.1 设置应用程序不可停止状态
下面的代码执行是向PMS中设置一个值,告诉系统,当前的广播处理已经到了这个状态,前面的前置判断条件已经成功,这时候应用程序不可能停止了,不可抗力的因素除外(电量不足等等)。
try {
AppGlobals.getPackageManager().setPackageStoppedState(
r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.curComponent.getPackageName() + ": " + e);
}
4.3.2 当前应用程序在运行
如果当前的应用程序在运行,那么直接处理当前的广播。
processCurBroadcastLocked(r, app, skipOomAdj);
这个处理函数中主要执行的步骤是:
- 更新ProcessRecord进程状态。
- 更新AMS中Lru 进程列表。
- 设置即将调用的intent
- 跨进程调用到ApplicationThread,从system_server进程到应用程序进程。
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
app.repProcState);
这个ApplicationThread是一个IApplicationThread binder对象:
private class ApplicationThread extends IApplicationThread.Stub {
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 r = new ReceiverData(intent, resultCode, data, extras,
sync, false, mAppThread.asBinder(), sendingUser);
r.info = info;
r.compatInfo = compatInfo;
sendMessage(H.RECEIVER, r);
}
}
发送一个H.RECEIVER信息到ActivityThread的handler中处理。最终在ActivityThread->handleReceiver(...)中处理。
private void handleReceiver(ReceiverData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
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();
if (data.info.splitName != null) {
context = (ContextImpl) context.createContextForSplit(data.info.splitName);
}
java.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) {
data.sendFinished(mgr);
}
try {
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
data.sendFinished(mgr);
} finally {
sCurrentBroadcastIntent.set(null);
}
if (receiver.getPendingResult() != null) {
data.finish();
}
}
这是广播最终处理的函数,这个函数主要执行两个步骤:
- 取出LoadedApk中的receiver,在广播注册的那一篇文章中已经写明这个receiver是如何设置的,请出门左转详细看看。
- 设置onReceive(...)回调,开发者也会直接在这个继承函数中执行广播的回调。