赞
踩
请支持原创~~
相关博文:
Android基础总结之五:BroadcastReceiver
protected-broadcast 规范使用(ERROR: Sending non-protected broadcast)
基于版本:Android O
上一篇博文 Android 中broadcast 注册过程解析 详细的解析了广播的注册过程,最终AMS 端的mRegisteredReceivers 保存了所有的动态注册进来的广播,并且一一对应ReceiverDispatcher。这一篇接着解析广播的另一半功能——发送过程。
用户可以通过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 接口,注意函数中传入的参数:
- 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,注意函数中传入的参数:
由于代码比较长,这里分段来解读。
- 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);
- }
- // 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)
这部分的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:
mStickyBroadcasts.put(userId, stickies);
其中根据参数sticky 来确定发送的时候为sticky 广播,如果是会添加到mStickyBroadcasts 列表中。
在Android 中broadcast 注册过程解析 我们也看到注册进来的广播也会先确认是否在mStickyBroadcasts 中是否存在。
- // 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 其实获取了两种广播:
其中有个判断很重要:
- 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() 后面会解析。
- // 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();
- }
- }
- 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() 这也是分发的核心所在。
这个函数是广播分发的核心所在,代码比较长,我们还是分段进行解析。
- if (fromMsg) {
- mBroadcastsScheduled = false;
- }
将mBroadcastsScheduled设为false是让下一条消息能发送。
- // First, deliver any non-serialized broadcasts right away.
- while (mParallelBroadcasts.size() > 0) {
- r = mParallelBroadcasts.remove(0);
- r.dispatchTime = SystemClock.uptimeMillis();
- r.dispatchClockTime = System.currentTimeMillis();
-
- final int N = r.receivers.size();
-
- for (int i=0; i<N; i++) {
- Object target = r.receivers.get(i);
- deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
- }
- addBroadcastToHistoryLocked(r);
- }
mParallelBroadcasts 上面我们在broadcastIntentLocked() 的时候说过,不管是fg 还是bg 最终的BroadRecord 都会保存在这里。
并行广播其实说法有点片面,称无序广播更好,因为这里map 排列的时候是没有顺序的 ,但是发布的时候还是要从第一个广播开始匹配。
详细deliverToRegisteredReceiverLocked() 函数看下面第5 点。
在处理串行广播之前有一段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越多则广播超时总时长越大。
- 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节。
这段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() 开始下一条广播分发。
- mPendingBroadcast = r;
- mPendingBroadcastRecvIndex = recIdx;
如果程序启动成功的话,会将广播pending,记录一下为了下一次的优先分发。
函数比较长,继续分段来解读。
- 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;
- }
- 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 发送广播的接口也有带权限的,这里要求接收端也必须具备发送端所要求的权限。
除了上面两个权限要求,还会根据当前广播的配对、是否killed等情况确认该广播是否最终放弃发布。
上面权限check 通过的话会进入最终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。
- 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 注册过程解析
- 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(),总结示意图如下:
- 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);
- }
- }
主要功能:
静态注册的广播接收者:
动态注册的广播接收者:
另外常量参数说明:
- 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。
- 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);
- }
- }
至此,广播的发布过程就解析完了。
静态广播:通过AndroidManifest.xml 注册的广播
动态广播:通过AMS.registerReceiver注册的广播,注意跟unregisterReceiver() 配套。
普通广播:sendBroadcast()
有序广播:sendOrderedBroadcast()
粘性广播:sendStickyBroadcast()
order = ture
order = false
ANR时机:只有串行广播才需要考虑超时,因为接收者是串行处理的,前一个receiver处理慢,会影响后一个receiver;并行广播 通过一个循环一次性向所有的receiver分发广播事件,所以不存在彼此影响的问题,则没有广播超时;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。