请支持原创~~
相关博文:
Android基础总结之五:BroadcastReceiver
protected-broadcast 规范使用(ERROR: Sending non-protected broadcast)
基于版本:Android O
0. 前言
上一篇博文 Android 中broadcast 注册过程解析 详细的解析了广播的注册过程,最终AMS 端的mRegisteredReceivers 保存了所有的动态注册进来的广播,并且一一对应ReceiverDispatcher。这一篇接着解析广播的另一半功能——发送过程。
1. context.sendBroadcast()
用户可以通过context 的接口进行广播的发布,系统会对其进行进一步的通知。接口比较多,如下:
public void sendBroadcast(Intent intent)
public void sendBroadcast(Intent intent, int userId)
public void sendBroadcast(Intent intent, String receiverPermission)
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广播。在上一篇博文 Android 中broadcast 注册过程解析 中已经详细的分析了sticky 广播在注册的时候执行流程。
下面我们挑其中比较经典常用的接口来解释:
@Override
public void sendBroadcast(Intent intent, String receiverPermission) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
null, false, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
将广播的intent 和receiver 想要接受此广播务必需要的权限,当然这个权限也可以为null,代表不收权限的限制。
最终会调用到AMS 中的broadcastIntent 接口,注意函数中传入的参数:
- 第1个参数是发送端的appThread
- 第2个参数是广播的intent
- 第5个参数是resultCode
- 第8个参数是receiver 所需要的权限
- 第9个参数是AppOps所需要的op 码
- 第10个参数是广播所携带的参数,Bundle形式
- 最后一个参数是user id(跟uid 不一样)
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); //传过来的thread会变成ProcessRecord
final int callingPid = Binder.getCallingPid(); //发送端的 pid
final int callingUid = Binder.getCallingUid(); //发送端的 uid
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;
}
}
同样最终会调用到broadcastIntentLocked,注意函数中传入的参数:
- 第1个参数为callerApp,发送端的ProcessRecord
- 第2个参数是发送端的 packageName
- 第3个参数是最开始传过来的广播的intent
- 第9个参数是接收端需要接收广播所需的permission
- 第10个参数是AppOps 所需要的op 码
- 最后几个参数是call 端的pid、uid和 user id。
3. AMS.broadcastIntentLocked()
由于代码比较长,这里分段来解读。
3.1 当广播有参数的时候,确认权限
BroadcastOptions brOptions = null;
if (bOptions != null) {
brOptions = new BroadcastOptions(bOptions);
if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
// See if the caller is allowed to do this. Note we are checking against
// the actual real caller (not whoever provided the operation as say a
// PendingIntent), because that who is actually supplied the arguments.
if (checkComponentPermission(
android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: " + intent.getAction()
+ " broadcast from " + callerPackage + " (pid=" + callingPid
+ ", uid=" + callingUid + ")"
+ " requires "
+ android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
}
}
这里的BroadcastOptions 就是处理第一步中第10个参数,一般没有的话为null。
public BroadcastOptions(Bundle opts) {
mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION);
mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0);
mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
Build.VERSION_CODES.CUR_DEVELOPMENT);
}
3.2 确认广播为protected 广播,而只有system code 才能发送protected 广播
// Verify that protected broadcasts are only being sent by system code,
// and that system code is only sending protected broadcasts.
final String action = intent.getAction();
final boolean isProtectedBroadcast;
try {
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception", e);
return ActivityManager.BROADCAST_SUCCESS;
}
final boolean isCallerSystem;
switch (UserHandle.getAppId(callingUid)) {
case ROOT_UID:
case SYSTEM_UID:
case PHONE_UID:
case BLUETOOTH_UID:
case NFC_UID:
isCallerSystem = true;
break;
default:
isCallerSystem = (callerApp != null) && callerApp.persistent;
break;
}
// First line security check before anything else: stop non-system apps from
// sending protected broadcasts.
if (!isCallerSystem) {
if (isProtectedBroadcast) {//非system code 不能发送protected 广播
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)) {
// Special case for compatibility: we don't want apps to send this,
// but historically it has not been protected and apps may be using it
// to poke their own app widget. So, instead of making it protected,
// just limit it to the caller.
if (callerPackage == null) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " from unknown caller.";
Slog.w(TAG, msg);
throw new SecurityException(msg);
} else if (intent.getComponent() != null) {
// They are good enough to send to an explicit component... verify
// it is being sent to the calling app.
if (!intent.getComponent().getPackageName().equals(
callerPackage)) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " to "
+ intent.getComponent().getPackageName() + " from "
+ callerPackage;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
} else {
// Limit broadcast to their own package.
intent.setPackage(callerPackage);
}
}
}
这一段要求,如果是protected broadcast,那么只能system uid 才能发送,对于非system uid 的app 不能发送protected broadcast。对于protected broadcast 的具体使用规范,可以看下:
protected-broadcast 规范使用(ERROR: Sending non-protected broadcast)
3.3 处理Package 相关的广播
这部分的code 比较多,这里不全部贴出来,主要是package 相关的广播,还有time、timezone 等广播,如下:
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:
case Intent.ACTION_PACKAGE_REPLACED:
case Intent.ACTION_PACKAGE_ADDED:
case Intent.ACTION_PACKAGE_DATA_CLEARED:
case Intent.ACTION_TIMEZONE_CHANGED:
case Intent.ACTION_TIME_CHANGED:
case Intent.ACTION_CLEAR_DNS_CACHE:
case Proxy.PROXY_CHANGE_ACTION:
case android.hardware.Camera.ACTION_NEW_PICTURE:
case android.hardware.Camera.ACTION_NEW_VIDEO:
3.4 如果是sticky 广播,添加到mStickyBroadcasts 列表中
mStickyBroadcasts.put(userId, stickies);
其中根据参数sticky 来确定发送的时候为sticky 广播,如果是会添加到mStickyBroadcasts 列表中。
在Android 中broadcast 注册过程解析 我们也看到注册进来的广播也会先确认是否在mStickyBroadcasts 中是否存在。
3.5 尝试向并行receivers递送广播
// Figure out who all will receive this broadcast.
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
// 收集注册的 receiver,这个应该是静态的广播
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
for (int i = 0; i < users.length; i++) {
if (mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
continue;
}
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, users[i]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
} else if (registeredReceiversForUser != null) {
registeredReceivers.addAll(registeredReceiversForUser);
}
}
} else {
// 获取动态注册的广播
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, userId);
}
}
final boolean replacePending =
(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
+ " replacePending=" + replacePending);
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
// 这个判断很重要
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
resultCode, resultData, resultExtras, ordered, sticky, false, userId);
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;
}
这段code 其实获取了两种广播:
- 通过collectReceiverComponents() 函数获取静态注册的广播
- 通过mReceiverResolver.queryIntent() 函数获取了动态注册的广播
其中有个判断很重要:
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
如果存在动态注册的广播,如果没有声明进行有序分发广播,就不会将这里的动态注册广播添加到静态注册的广播list 中。下面会解释。
如果没有声明是有序分发广播,这里的动态注册广播会以并行广播进行分发(详见scheduleBroadcastsLocked())。
另外,这里会new一个BroadcastRecord节点,并插入BroadcastQueue内的并行处理队列,最后发起实际的广播调度(scheduleBroadcastsLocked())。
其实不光并行处理部分需要一个BroadcastRecord节点,串行处理部分也需要BroadcastRecord节点。也就是说,要激发一次广播,AMS必须构造一个或两个BroadcastRecord节点,并将之插入合适的广播队列(mFgBroadcastQueue或mBgBroadcastQueue)。插入成功后,再执行队列的scheduleBroadcastsLocked()动作,进行实际的派发调度。
如果不是有序广播,最后会在并行分发完成之后重置:
registeredReceivers = null;
NR = 0;
这一部分的code 暂时先解析到这里,其中关键的分发函数scheduleBroadcastsLocked() 后面会解析。
3.6 尝试逐个向串行receivers递送广播
// 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++;
}
首先来看下这段code,上面3.5 中说到如果是有序广播,动态注册的广播并不会立刻分发,而是会到这里跟这里receivers list进行有效的合并。
下面来看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);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
"Enqueueing broadcast " + r.intent.getAction());
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 {
// 将BroadcastRecord 添加到串行广播列表中
queue.enqueueOrderedBroadcastLocked(r);
// 进行最后的分发工作
queue.scheduleBroadcastsLocked();
}
}
3.7 广播的递送
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;
}
进行了异步操作,如果这个分发的指令刚发送完,下一次再来会直接return,最终会调用 processNextBroadcast() 这也是分发的核心所在。
4. BroadcastQueue.processNextBroadcast()
这个函数是广播分发的核心所在,代码比较长,我们还是分段进行解析。
4.1 重置mBroadcastsScheduled
if (fromMsg) {
mBroadcastsScheduled = false;
}
将mBroadcastsScheduled设为false是让下一条消息能发送。
4.2 立刻解析并行广播
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
final int N = r.receivers.size();
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
}
mParallelBroadcasts 上面我们在broadcastIntentLocked() 的时候说过,不管是fg 还是bg 最终的BroadRecord 都会保存在这里。
并行广播其实说法有点片面,称无序广播更好,因为这里map 排列的时候是没有顺序的 ,但是发布的时候还是要从第一个广播开始匹配。
详细deliverToRegisteredReceiverLocked() 函数看下面第5 点。
4.3 处理串行广播
在处理串行广播之前有一段code:
if (mPendingBroadcast != null) {
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
"processNextBroadcast [" + mQueueName + "]: waiting for "
+ mPendingBroadcast.curApp);
boolean isDead;
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.crashing;
}
if (!isDead) {
// It's still alive, so keep waiting
return;
} else {
Slog.w(TAG, "pending app ["
+ mQueueName + "]" + mPendingBroadcast.curApp
+ " died before responding to broadcast");
mPendingBroadcast.state = BroadcastRecord.IDLE;
mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
}
}
最开始发广播的时候可能程序并没有起来,mPendingBroadcast 保存的是上一次分发给应用还没有start 的广播。详细看下面4.6 节。
do {
if (mOrderedBroadcasts.size() == 0) {
// 最后一条广播处理会gc 一下
mService.scheduleAppGcsLocked();
if (looped) {
// 最后一条处理会更新所有进程oom
mService.updateOomAdjLocked();
}
return;
}
// 如果有序广播还有,get 第一个开始处理
r = mOrderedBroadcasts.get(0);
boolean forceReceive = false;
// 这里处理广播的ANR
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;
}
}
...
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
// No more receivers for this broadcast! Send the final
// result if requested...
if (r.resultTo != null) {
try {
//处理广播消息消息,调用到onReceive()
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);
}
}
//取消BROADCAST_TIMEOUT_MSG消息
cancelBroadcastTimeoutLocked();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
"Finished with ordered broadcast " + r);
// ... and on to the next...
addBroadcastToHistoryLocked(r);
mOrderedBroadcasts.remove(0);
r = null;
looped = true;
continue;
}
} while (r == null);
mTimeoutPeriod,对于前台广播则为10s,对于后台广播则为60s。广播超时为2*mTimeoutPeriod*numReceivers,接收者个数numReceivers越多则广播超时总时长越大。
4.4 处理动态注册的有序广播
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 (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(filter.owningUid,
brOptions.getTemporaryAppWhitelistDuration(), r);
}
}
return;
}
之前在 3.2 节中讲过,对于动态注册的广播首先确定是否是以有序广播的形式发出,如果是以有序广播的形式,最后不会调用deliverToRegisteredReceiverLocked() 函数,而是添加到receivers 列表中。这里可以看到,动态注册的广播最终还是会通过deliverToRegisteredReceiverLocked() 函数进行分发。详细看下面的第 5节。
4.5 处理静态注册的有序广播
这段code 太长,暂时不贴出来了,最开始的时候会进行权限的check,跟下面第 5 节分发并行广播差不多。
其中有意思的一段:
if (!skip) {
skip = !mService.isAutoStartAllowed(info.activityInfo.applicationInfo.uid, info.activityInfo.applicationInfo.packageName);
}
AMS 中对于应用有这样一个自启动功能的验证。(我之前做自启动管理的时候并没有发现还有这个,失败~~)
继续顺着code 往下解读,最终发现有序广播的处理code:
// Is this receiver's application already running?
if (app != null && app.thread != null && !app.killed) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
processCurBroadcastLocked(r, app);
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.
}
注意,这段code 条件指的是应用已经运行。终于找到了有序广播的分发接口processCurBroadcastLocked()。
当程序没有运行的时候会执行下面这段code:
if ((r.curApp=mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
"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 + "/"
+ info.activityInfo.applicationInfo.uid + " 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;
}
直接finishReceiverLocked(),并继续执行scheduleBroadcastsLocked() 开始下一条广播分发。
4.6 广播没发出去,继续下一轮分发
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
如果程序启动成功的话,会将广播pending,记录一下为了下一次的优先分发。
5. deliverToRegisteredReceiverLocked()
函数比较长,继续分段来解读。
5.1 check 发送端是否拥有接收端要求的权限
if (filter.requiredPermission != null) {
int perm = mService.checkComponentPermission(filter.requiredPermission,
r.callingPid, r.callingUid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Permission Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid="
+ r.callingPid + ", uid=" + r.callingUid + ")"
+ " requires " + filter.requiredPermission
+ " due to registered receiver " + filter);
skip = true;
} else {
final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
if (opCode != AppOpsManager.OP_NONE
&& mService.mAppOpsService.noteOperation(opCode, r.callingUid,
r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid="
+ r.callingPid + ", uid=" + r.callingUid + ")"
+ " requires appop " + AppOpsManager.permissionToOp(
filter.requiredPermission)
+ " due to registered receiver " + filter);
skip = true;
}
}
}
Android 中broadcast 注册过程解析 一文中我们说到register 广播的接口比较多,其中有一个带权限的。这个就要求发送端需要具备这个权限。系统中确认这个权限的时候有两种方式,一个是通过PMS 中的runtime permission,另一个是通过AppOps的正常权限。
如果这里没有给出规定的权限,这个广播是无法发布出去的。
if (skip) {
r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
return;
}
5.2 check 接收端是否拥有发送端要求的权限
if (!skip && r.requiredPermissions != null && r.requiredPermissions.length > 0) {
for (int i = 0; i < r.requiredPermissions.length; i++) {
String requiredPermission = r.requiredPermissions[i];
int perm = mService.checkComponentPermission(requiredPermission,
filter.receiverList.pid, filter.receiverList.uid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Permission Denial: receiving "
+ r.intent.toString()
+ " to " + filter.receiverList.app
+ " (pid=" + filter.receiverList.pid
+ ", uid=" + filter.receiverList.uid + ")"
+ " 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,
filter.receiverList.uid, filter.packageName)
!= AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving "
+ r.intent.toString()
+ " to " + filter.receiverList.app
+ " (pid=" + filter.receiverList.pid
+ ", uid=" + filter.receiverList.uid + ")"
+ " requires appop " + AppOpsManager.permissionToOp(
requiredPermission)
+ " due to sender " + r.callerPackage
+ " (uid " + r.callingUid + ")");
skip = true;
break;
}
}
}
同上面5.1 发送广播的接口也有带权限的,这里要求接收端也必须具备发送端所要求的权限。
5.3 check 其他权限
除了上面两个权限要求,还会根据当前广播的配对、是否killed等情况确认该广播是否最终放弃发布。
上面权限check 通过的话会进入最终performReceiveLocked() 函数。
5.4 performReceiveLocked()
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
if (app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
try {
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.repProcState);
} catch (RemoteException ex) {
synchronized (mService) {
Slog.w(TAG, "Can't deliver broadcast to " + app.processName
+ " (pid " + app.pid + "). Crashing it.");
app.scheduleCrash("can't deliver broadcast");
}
throw ex;
}
} else {
// Application has died. Receiver doesn't exist.
throw new RemoteException("app.thread must not be null");
}
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
这里首先判断通过注册进来的广播是什么,因为例子中是通过activity注册的,这里的app参数就是代表activity的进程记录块,receiver这是注册时传给AMS的Binder对象。
在调用后会通过:app.thread.scheduleRegisteredReceiver()函数把广播分发给activity。
5.5 scheduleRegisteredReceiver()
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState) throws RemoteException {
updateProcessState(processState, false);
receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
sticky, sendingUser);
}
终于看到了这里的receiver 对象,这个是在deliverToRegisteredReceiverLocked() 函数中传入的。
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
这里就不细细解释,详细看Android 中broadcast 注册过程解析
5.6 performReceive()
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
if (intent == null) {
Log.wtf(TAG, "Null intent received");
} else {
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction()
+ " seq=" + seq + " to " + mReceiver);
}
}
if (intent == null || !mActivityThread.post(args.getRunnable())) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManager.getService();
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing sync broadcast to " + mReceiver);
args.sendFinished(mgr);
}
}
}
其中会调用mActivityThread.post(args.getRunnable()),最终调用onReceiver():
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
终于看到了onReceiver(),总结示意图如下:
5.7 finish()
if (receiver.getPendingResult() != null) {
finish();
}
public final void finish() {
if (mType == TYPE_COMPONENT) { // 静态注册的广播
final IActivityManager mgr = ActivityManager.getService();
if (QueuedWork.hasPendingWork()) {
QueuedWork.queue(new Runnable() {
@Override public void run() {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast after work to component " + mToken);
sendFinished(mgr);
}
}, false);
} else {
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {// 动态注册的广播
final IActivityManager mgr = ActivityManager.getService();
sendFinished(mgr);
}
}
主要功能:
静态注册的广播接收者:
- 当QueuedWork工作未完成, 即SharedPreferences写入磁盘的操作没有完成, 则等待完成再执行sendFinished方法;
- 当QueuedWork工作已完成, 则直接调用sendFinished方法;
动态注册的广播接收者:
- 当发送的是串行广播, 则直接调用sendFinished方法。
另外常量参数说明:
- TYPE_COMPONENT: 静态注册
- TYPE_REGISTERED: 动态注册
- TYPE_UNREGISTERED: 取消注册
5.8 sendFinished()
public void sendFinished(IActivityManager am) {
synchronized (this) {
if (mFinished) {
throw new IllegalStateException("Broadcast already finished");
}
mFinished = true; // 置标志,防止 2 次finish
try {
if (mResultExtras != null) {
mResultExtras.setAllowFds(false);
}
if (mOrderedHint) { // 有序广播
am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
mAbortBroadcast, mFlags);
} else { // 无序广播
// This broadcast was sent to a component; it is not ordered,
// but we still need to tell the activity manager we are done.
am.finishReceiver(mToken, 0, null, null, false, mFlags);
}
} catch (RemoteException ex) {
}
}
}
其实从这里的mFinished 的标志可以看到其实这里的处理基本是完成了,最后还是会通过am.finishReceiver 来通知AMS 在LoadedApk(注册端) 中的广播处理工作完成。由于是IPC,通过的是AMS 的binder。
5.9 finishReceiver()
public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + who);
// Refuse possible leaked file descriptors
if (resultExtras != null && resultExtras.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Bundle");
}
final long origId = Binder.clearCallingIdentity();
try {
boolean doNext = false;
BroadcastRecord r;
synchronized(this) {
BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
? mFgBroadcastQueue : mBgBroadcastQueue;
r = queue.getMatchingOrderedReceiver(who);
if (r != null) {
doNext = r.queue.finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, true);
}
}
// 如果还有其他广播,继续执行,不过这里不是从handler 发消息,参数为false
if (doNext) {
r.queue.processNextBroadcast(false);
}
trimApplications();
} finally {
Binder.restoreCallingIdentity(origId);
}
}
至此,广播的发布过程就解析完了。
6. 总结
6.1 BroadcatReceiver 分类
静态广播:通过AndroidManifest.xml 注册的广播
动态广播:通过AMS.registerReceiver注册的广播,注意跟unregisterReceiver() 配套。
6.2 send broadcast 分类
普通广播:sendBroadcast()
有序广播:sendOrderedBroadcast()
粘性广播:sendStickyBroadcast()
6.3 广播处理机制
order = ture
- 静态注册的广播:串行处理
- 动态注册的广播:串行处理
order = false
- 静态注册的广播:串行处理
- 动态注册的广播:并行处理
6.4 广播中的ANR
ANR时机:只有串行广播才需要考虑超时,因为接收者是串行处理的,前一个receiver处理慢,会影响后一个receiver;并行广播 通过一个循环一次性向所有的receiver分发广播事件,所以不存在彼此影响的问题,则没有广播超时;
- 串行广播超时情况1:某个广播总处理时间 > 2* receiver总个数 * mTimeoutPeriod, 其中mTimeoutPeriod,前台队列默认为10s,后台队列默认为60s;
- 串行广播超时情况2:某个receiver的执行时间超过mTimeoutPeriod;