赞
踩
在学习Broadcast timeout 之前最好对广播的发生流程有一定了解,可以参考gityuan大神《Android Broadcast广播机制分析》。在广播流程有基础后,我们再来看最关键的代码块processNextBroadcastLocked,我们同样提两个问题:1.broadcast 什么情况下会ANR ? 2.ANR会有哪些log。
- 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();//更新cpu状态
- //fromMsg是指processNextBroadcast()是否由BroadcastHandler所调用的. 第一次从sendBroadcast是true。
- if (fromMsg) {
- mBroadcastsScheduled = false;
- }
-
- // First, deliver any non-serialized broadcasts right away.
- //part1: 处理并行广播,并行广播没有ANR的问题。
- while (mParallelBroadcasts.size() > 0) {
- r = mParallelBroadcasts.remove(0);//获取广播。
- r.dispatchTime = SystemClock.uptimeMillis();
- r.dispatchClockTime = System.currentTimeMillis();
-
- 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));
- }
-
- final int N = r.receivers.size();
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
- + mQueueName + "] " + r);
- for (int i=0; i<N; i++) {
- Object target = r.receivers.get(i);
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Delivering non-ordered on [" + mQueueName + "] to registered "
- + target + ": " + r);
- //分发广播给已注册的receive
- deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
- }
- addBroadcastToHistoryLocked(r);//将广播添加历史统计
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
- + mQueueName + "] " + r);
- }
-
-
-
- // Now take care of the next serialized one...
-
- // If we are waiting for a process to come up to handle the next
- // broadcast, then do nothing at this point. Just in case, we
- // check that the process we're waiting for still exists.
- //mPendingBroadcast 表示的是等待进程启动来处理的广播,如果进程存活则直接 return 继续等待
- //如果该进程已经被杀,则重置 mPendingBroadcast 为空
- if (mPendingBroadcast != null) {
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
- "processNextBroadcast [" + mQueueName + "]: waiting for "
- + mPendingBroadcast.curApp);
-
- boolean isDead;
- if (mPendingBroadcast.curApp.pid > 0) {
- synchronized (mService.mPidsSelfLocked) {
- ProcessRecord proc = mService.mPidsSelfLocked.get(
- mPendingBroadcast.curApp.pid);
- isDead = proc == null || proc.isCrashing();
- }
- } else {
- final ProcessRecord proc = mService.mProcessList.mProcessNames.get(
- mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
- isDead = proc == null || !proc.pendingStart;
- }
- if (!isDead) {
- // It's still alive, so keep 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;
- }
- }
-
- boolean looped = false;
-
-
- // 这后面的都是处理有序广播
- do {
- final long now = SystemClock.uptimeMillis();
- r = mDispatcher.getNextBroadcastLocked(now);//从有序广播list中取出一个广播。
-
- if (r == null) {//如果是空的话,说明没有广播需要处理,就返回。
- // No more broadcasts are deliverable right now, so all done!
- mDispatcher.scheduleDeferralCheckLocked(false);
- mService.scheduleAppGcsLocked();//所有串行广播处理完成,则调度执行gc
- 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 logging
- if (mService.mUserController.mBootCompleted && mLogLatencyMetrics) {
- mLogLatencyMetrics = false;
- }
-
- return;
- }
-
- boolean forceReceive = false;
-
- // Ensure that even if something goes awry with the timeout
- // detection, we catch "hung" broadcasts here, discard them,
- // and continue to make progress.
- //
- // This is only done if the system is ready so that early-stage receivers
- // don't get executed with timeouts; and of course other timeout-
- // exempt broadcasts are ignored.
- //如果广播处理的总时间超过2 * mConstants.TIMEOUT * numReceivers ,broadcastTimeoutLocked 也就ANR.
- //所以在处理有序广播的时候不仅对单个广播时长有做限制,对于总的Receivers时间也有限制。
- 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)) &&
- /// M: ANR Debug Mechanism
- !mService.mAnrManager.isAnrDeferrable()) {
- 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 broadcast
- forceReceive = 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?
- //如果广播接收者为空、下一个接收者下标越界、广播被中止等则继续分发下一个广播 ,r.receivers 什么时候会是null,finishReceiverLocked
- if (r.receivers == null || r.nextReceiver >= numReceivers
- || r.resultAbort || forceReceive) {
- // Send the final result if requested
- if (r.resultTo != null) {
- boolean sendResult = true;
-
- // if this was part of a split/deferral complex, update the refcount and only
- // send the completion when we clear all of them
- if (r.splitToken != 0) {
- int newCount = mSplitRefcounts.get(r.splitToken) - 1;
- if (newCount == 0) {
- // done! clear out this record's bookkeeping and deliver
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST,
- "Sending broadcast completion for split token "
- + r.splitToken + " : " + r.intent.getAction());
- }
- mSplitRefcounts.delete(r.splitToken);
- } else {
- // still have some split broadcast records in flight; update refcount
- // and hold off on the callback
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST,
- "Result refcount now " + newCount + " for split token "
- + r.splitToken + " : " + r.intent.getAction()
- + " - not sending completion yet");
- }
- sendResult = false;
- mSplitRefcounts.put(r.splitToken, newCount);
- }
- }
- if (sendResult) {
- try {
- if (DEBUG_BROADCAST) {
- Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
- + r.intent.getAction() + " app=" + r.callerApp);
- }
- //处理广播消息消息,调用到onReceive()
- performReceiveLocked(r.callerApp, r.resultTo,
- new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false, r.userId);
- // Set this to null so that the reference
- // (local and remote) isn't kept in the mBroadcastHistory.
- r.resultTo = null;
- } catch (RemoteException e) {
- r.resultTo = null;
- Slog.w(TAG, "Failure ["
- + mQueueName + "] sending broadcast result of "
- + r.intent, e);
- }
- }
- }
-
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
- //取消BROADCAST_TIMEOUT_MSG消息
- cancelBroadcastTimeoutLocked();
-
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
- "Finished with ordered broadcast " + r);
-
- // ... and on to the next...
- addBroadcastToHistoryLocked(r);//已经发过的广播添加到list
- if (r.intent.getComponent() == null && r.intent.getPackage() == null
- && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
- // This was an implicit broadcast... let's record it for posterity.
- mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
- r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
- }
- mDispatcher.retireBroadcastLocked(r);
- r = null;
- looped = true;
- continue;
- }
-
- // Check whether the next receiver is under deferral policy, and handle that
- // accordingly. If the current broadcast was already part of deferred-delivery
- // tracking, we know that it must now be deliverable as-is without re-deferral.
- if (!r.deferred) {
- final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
- if (mDispatcher.isDeferringLocked(receiverUid)) {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Next receiver in " + r + " uid " + receiverUid
- + " at " + r.nextReceiver + " is under deferral");
- }
- // If this is the only (remaining) receiver in the broadcast, "splitting"
- // doesn't make sense -- just defer it as-is and retire it as the
- // currently active outgoing broadcast.
- BroadcastRecord defer;
- if (r.nextReceiver + 1 == numReceivers) {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Sole receiver of " + r
- + " is under deferral; setting aside and proceeding");
- }
- defer = r;
- mDispatcher.retireBroadcastLocked(r);
- } else {
- // Nontrivial case; split out 'uid's receivers to a new broadcast record
- // and defer that, then loop and pick up continuing delivery of the current
- // record (now absent those receivers).
-
- // The split operation is guaranteed to match at least at 'nextReceiver'
- 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 relevant
- if (r.resultTo != null) {
- int token = r.splitToken;
- if (token == 0) {
- // first split of this record; refcount for 'r' and 'deferred'
- r.splitToken = defer.splitToken = nextSplitTokenLocked();
- mSplitRefcounts.put(r.splitToken, 2);
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST,
- "Broadcast needs split refcount; using new token "
- + r.splitToken);
- }
- } else {
- // new split from an already-refcounted situation; increment count
- final int curCount = mSplitRefcounts.get(token);
- if (DEBUG_BROADCAST_DEFERRAL) {
- if (curCount == 0) {
- Slog.wtf(TAG_BROADCAST,
- "Split refcount is zero with token for " + r);
- }
- }
- mSplitRefcounts.put(token, curCount + 1);
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "New split count for token " + token
- + " is " + (curCount + 1));
- }
- }
- }
- }
- mDispatcher.addDeferredBroadcast(receiverUid, defer);
- r = null;
- looped = true;
- continue;
- }
- }
- } while (r == null);
-
- // Get the next receiver...
- // recIdx 表示当前接收者的下标,r.nextReceiver 为下一个接收者的下标,默认为 0
- 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;//设置分发时间,这个时间对timeout比较关键。
- r.dispatchClockTime = System.currentTimeMillis();
-
- if (mLogLatencyMetrics) {
- StatsLog.write(
- StatsLog.BROADCAST_DISPATCH_LATENCY_REPORTED,
- r.dispatchClockTime - r.enqueueClockTime);
- }
-
- 打印一些trace 信息。一般情况是不开启打印的。eng版本才有开启。
- 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 (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
- + mQueueName + "] " + r);
- }
- //mPendingBroadcastTimeoutMessage 这个值在刚才执行cancelBroadcastTimeoutLocked 设置false。
- 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);//在10s 后发送BROADCAST_TIMEOUT_MSG 广播,最终会执行broadcastTimeoutLocked。后面有对这个方法说明。
- }
-
- 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);
- // r is guaranteed ordered at this point, so we know finishReceiverLocked()
- // will get a callback and handle the activity start token lifecycle.
- }
- 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)) {
- Slog.w(TAG, "Association not allowed: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ") to " + component.flattenToShortString());
- skip = true;
- }
- if (!skip) {
- skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
- r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
- if (skip) {
- Slog.w(TAG, "Firewall blocked: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ") to " + component.flattenToShortString());
- }
- }
- 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) {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ")"
- + " is not exported from uid " + info.activityInfo.applicationInfo.uid
- + " due to receiver " + component.flattenToShortString());
- } else {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ")"
- + " requires " + info.activityInfo.permission
- + " due to receiver " + component.flattenToShortString());
- }
- 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) {
- Slog.w(TAG, "Appop Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid="
- + r.callingPid + ", uid=" + r.callingUid + ")"
- + " requires appop " + AppOpsManager.permissionToOp(
- info.activityInfo.permission)
- + " due to registered receiver "
- + component.flattenToShortString());
- skip = true;
- }
- }
- if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
- r.requiredPermissions != null && r.requiredPermissions.length > 0) {
- for (int i = 0; i < r.requiredPermissions.length; i++) {
- String requiredPermission = r.requiredPermissions[i];
- try {
- perm = AppGlobals.getPackageManager().
- checkPermission(requiredPermission,
- info.activityInfo.applicationInfo.packageName,
- UserHandle
- .getUserId(info.activityInfo.applicationInfo.uid));
- } catch (RemoteException e) {
- perm = PackageManager.PERMISSION_DENIED;
- }
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires " + requiredPermission
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
- if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
- && mService.mAppOpsService.noteOperation(appOp,
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires appop " + AppOpsManager.permissionToOp(
- requiredPermission)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- }
- }
- if (!skip && r.appOp != AppOpsManager.OP_NONE
- && mService.mAppOpsService.noteOperation(r.appOp,
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires appop " + AppOpsManager.opToName(r.appOp)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- boolean isSingleton = false;
- try {
- isSingleton = mService.isSingleton(info.activityInfo.processName,
- info.activityInfo.applicationInfo,
- info.activityInfo.name, info.activityInfo.flags);
- } catch (SecurityException e) {
- Slog.w(TAG, e.getMessage());
- skip = true;
- }
- if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
- if (ActivityManager.checkUidPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS,
- info.activityInfo.applicationInfo.uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
- + " requests FLAG_SINGLE_USER, but app does not hold "
- + android.Manifest.permission.INTERACT_ACROSS_USERS);
- 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 receiver
- Slog.w(TAG, "Exception getting recipient info for "
- + info.activityInfo.packageName, e);
- }
- if (!isAvailable) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Skipping delivery to " + info.activityInfo.packageName + " / "
- + info.activityInfo.applicationInfo.uid
- + " : package no longer available");
- skip = true;
- }
- }
-
- // If permissions need a review before any of the app components can run, we drop
- // the broadcast and if the calling app is in the foreground and the broadcast is
- // explicit we launch the review UI passing it a pending intent to send the skipped
- // broadcast.
- if (!skip) {
- if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
- info.activityInfo.packageName, UserHandle.getUserId(
- info.activityInfo.applicationInfo.uid))) {
- skip = true;
- }
- }
-
- // This is safe to do even if we are skipping the broadcast, and we need
- // this information now to evaluate whether it is going to be allowed to run.
- final int receiverUid = info.activityInfo.applicationInfo.uid;
- // If it's a singleton, it needs to be the same app or a special app
- if (r.callingUid != Process.SYSTEM_UID && isSingleton
- && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
- info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
- }
- //该receiver所对应的进程已经运行,则直接处理
- String targetProcess = info.activityInfo.processName;
- ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
- info.activityInfo.applicationInfo.uid, false);
-
- if (!skip) {
- final int allowed = mService.getAppStartModeLocked(
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
- info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
- if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
- // We won't allow this receiver to be launched if the app has been
- // completely disabled from launches, or it was not explicitly sent
- // to it and the app is in a state that should not receive it
- // (depending on how getAppStartModeLocked has determined that).
- if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
- Slog.w(TAG, "Background execution disabled: receiving "
- + r.intent + " to "
- + component.flattenToShortString());
- skip = true;
- } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
- || (r.intent.getComponent() == null
- && r.intent.getPackage() == null
- && ((r.intent.getFlags()
- & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
- && !isSignaturePerm(r.requiredPermissions))) {
- mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
- component.getPackageName());
- Slog.w(TAG, "Background execution not allowed: receiving "
- + r.intent + " to "
- + component.flattenToShortString());
- skip = true;
- }
- }
- }
-
- if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())
- && !mService.mUserController
- .isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid),
- 0 /* flags */)) {
- skip = true;
- Slog.w(TAG,
- "Skipping delivery to " + info.activityInfo.packageName + " / "
- + info.activityInfo.applicationInfo.uid + " : user is not running");
- }
-
- /// M: DuraSpeed @{
- boolean isSuppress = false;
- isSuppress = mService.mAmsExt.onBeforeStartProcessForStaticReceiver(
- info.activityInfo.packageName);
- if (isSuppress) {
- skip = true;
- }
- /// @}
-
- /// M: For ago performance
- if (!mService.mAmsExt.isComponentNeedsStart(info.activityInfo.packageName,
- "static_broadcast")) {
- skip = true;
- }
-
- if (skip) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Skipping delivery of ordered [" + mQueueName + "] "
- + r + " for reason described above");
- r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
- r.receiver = null;
- r.curFilter = null;
- r.state = BroadcastRecord.IDLE;
- r.manifestSkipCount++;
- scheduleBroadcastsLocked();
- return;
- }
- r.manifestCount++;
-
- r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
- r.state = BroadcastRecord.APP_RECEIVE;
- r.curComponent = component;
- r.curReceiver = info.activityInfo;
- if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
- Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
- + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
- + receiverUid);
- }
-
- if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
- scheduleTempWhitelistLocked(receiverUid,
- brOptions.getTemporaryAppWhitelistDuration(), r);
- }
-
- // Broadcast is being executed, its package can't be stopped.
- try {
- AppGlobals.getPackageManager().setPackageStoppedState(
- r.curComponent.getPackageName(), false, r.userId);
- } catch (RemoteException e) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + r.curComponent.getPackageName() + ": " + e);
- }
-
- // Is this receiver's application already running?
- if (app != null && app.thread != null && !app.killed) {
- try {
- app.addPackage(info.activityInfo.packageName,
- info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
- maybeAddAllowBackgroundActivityStartsToken(app, r);
- processCurBroadcastLocked(r, app, skipOomAdj);
- return;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when sending broadcast to "
- + r.curComponent, e);
- } catch (RuntimeException e) {
- Slog.wtf(TAG, "Failed sending broadcast to "
- + r.curComponent + " with " + r.intent, e);
- // If some unexpected exception happened, just skip
- // this broadcast. At this point we are not in the call
- // from a client, so throwing an exception out from here
- // will crash the entire system instead of just whoever
- // sent the broadcast.
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
- // We need to reset the state if we failed to start the receiver.
- r.state = BroadcastRecord.IDLE;
- return;
- }
-
- // If a dead object exception was thrown -- fall through to
- // restart the application.
- }
-
- // Not running -- get it started, to be executed when the app comes up.
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Need to start app ["
- + mQueueName + "] " + targetProcess + " for broadcast " + r);
- //该receiver所对应的进程尚未启动,则创建该进程
- if ((r.curApp=mService.startProcessLocked(targetProcess,
- info.activityInfo.applicationInfo, true,
- r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
- new HostingRecord("broadcast", r.curComponent),
- (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
- == null) {
-
- // Ah, this recipient is unavailable. Finish it if necessary,
- // and mark the broadcast record as ready for the next.
-
- //启动失败的情况
- Slog.w(TAG, "Unable to launch app "
- + info.activityInfo.applicationInfo.packageName + "/"
- + receiverUid + " for broadcast "
- + r.intent + ": process is bad");
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
- r.state = BroadcastRecord.IDLE;
- return;
- }
-
- maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
- mPendingBroadcast = r;
- mPendingBroadcastRecvIndex = recIdx;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
上面的代码很长总的可以分为两部分:1.处理并行广播的情况。2 .处理串行广播的情况,也就我们说的有序广播。 而ANR只有是有序广播的时候才会发生。
归纳是:1.while 循环先把并行广播处理完成。
2. 从mDispatcher.getNextBroadcastLocked 获取 BroadcastRecord实例,如果是空的话,说明没有广播需要处理,就返回。
3.对总的 numReceivers时间进行判断,判断依据是:2 * mConstants.TIMEOUT * numReceivers 。如果超过这个时间就broadcastTimeoutLocked,产生ANR。系统不仅对单个receiver的时间有管控,总的receivers时间也有管控。
4 . cancelBroadcastTimeoutLocked,取消超时广播,这个对广播第一个receiver来说没什么意义,主要是对第二个之后的receiver的影响。
5. 如果是第一个receiver的话就把分发时间付给它。 if (recIdx == 0) {r.dispatchTime = r.receiverTime}。
6 .发生一个延迟广播 setBroadcastTimeoutLocked(timeoutTime),时间是10s ,如果超过这个时间还没有执行cancelBroadcastTimeoutLocked 。就会引发ANR。那么提个问题,什么情况下会执行cancelBroadcastTimeoutLocked 。答案是,当广播结束完会调用AMS finishReceiver方法,nextreceiver ,执行 r.queue.processNextBroadcastLocked。由回到processNextBroadcastLocked方法中,然后就会调用cancelBroadcastTimeoutLocked
7.如果是动态广播,就比较简单,执行deliverToRegisteredReceiverLocked,静态广播 要判断各种权限问题,还需要判断广播的进程是否存在,如果不存在需要创建新的进程。
- 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;
- BroadcastQueue queue;
-
- synchronized(this) {
- if (isOnOffloadQueue(flags)) {
- queue = mOffloadBroadcastQueue;
- } else {
- 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);
- }
- if (doNext) {
- r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true);
- }
- // updateOomAdjLocked() will be done here
- trimApplicationsLocked(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
- }
-
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- final void broadcastTimeoutLocked(boolean fromMsg) {
- if (fromMsg) {
- mPendingBroadcastTimeoutMessage = false;
- /// M: ANR Debug Mechanism
- mService.mAnrManager.removeBroadcastMonitorMessage();
- }
- //有序广播已经执行结束,或者广播接受者已经finish了。
- if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
- return;
- }
-
- long now = SystemClock.uptimeMillis();
- BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
- if (fromMsg) {
- /// M: ANR Debug Mechanism @{
- if (mService.mAnrManager.isAnrDeferrable()) {
- setBroadcastTimeoutLocked(SystemClock.uptimeMillis() + mConstants.TIMEOUT);
- return;
- } /// @}
- if (!mService.mProcessesReady) {
- // Only process broadcast timeouts if the system is ready; some early
- // broadcasts do heavy work setting up system facilities
- return;
- }
-
- // If the broadcast is generally exempt from timeout tracking, we're done
- if (r.timeoutExempt) {
- if (DEBUG_BROADCAST) {
- Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
- + r.intent.getAction());
- }
- return;
- }
-
- long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
- //如果现在的时间小于超时时间,说明还没有超时,就再执行setBroadcastTimeoutLocked,TIMEOUT=10s
- if (timeoutTime > now) {
- // We can observe premature timeouts because we do not cancel and reset the
- // broadcast timeout message after each receiver finishes. Instead, we set up
- // an initial timeout then kick it down the road a little further as needed
- // when it expires.
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Premature timeout ["
- + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
- + timeoutTime);
- setBroadcastTimeoutLocked(timeoutTime);
- return;
- }
- }
- //已经finish receiver ,也就是在执行 finishReceiverLocked方法时
- if (r.state == BroadcastRecord.WAITING_SERVICES) {
- // In this case the broadcast had already finished, but we had decided to wait
- // for started services to finish as well before going on. So if we have actually
- // waited long enough time timeout the broadcast, let's give up on the whole thing
- // and just move on to the next.
- Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
- ? r.curComponent.flattenToShortString() : "(null)"));
- r.curComponent = null;
- r.state = BroadcastRecord.IDLE;
- processNextBroadcast(false);
- return;
- }
-
- // If the receiver app is being debugged we quietly ignore unresponsiveness, just
- // tidying up and moving on to the next broadcast without crashing or ANRing this
- // app just because it's stopped at a breakpoint.
- final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
-
- Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
- + ", started " + (now - r.receiverTime) + "ms ago");
- r.receiverTime = now;
- if (!debugging) {
- r.anrCount++;
- }
-
- ProcessRecord app = null;
- String anrMessage = null;
-
- Object curReceiver;
- if (r.nextReceiver > 0) {
- curReceiver = r.receivers.get(r.nextReceiver-1);
- r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
- } else {
- curReceiver = r.curReceiver;
- }
- Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
- logBroadcastReceiverDiscardLocked(r);
- //查询App进程
- if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
- BroadcastFilter bf = (BroadcastFilter)curReceiver;
- if (bf.receiverList.pid != 0
- && bf.receiverList.pid != ActivityManagerService.MY_PID) {
- synchronized (mService.mPidsSelfLocked) {
- app = mService.mPidsSelfLocked.get(
- bf.receiverList.pid);
- }
- }
- } else {
- app = r.curApp;
- }
-
- if (app != null) {//如果进程还存在没有被杀死的话创建anrMessage
- anrMessage = "Broadcast of " + r.intent.toString();
- }
-
- if (mPendingBroadcast == r) {
- mPendingBroadcast = null;
- }
-
- // Move on to the next receiver.
- //关闭receiver 启动下一个receiver
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
-
- 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));
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- //向event log 里面写log,关键字是:am_broadcast_discard_app
- final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
- final int logIndex = r.nextReceiver - 1;
- if (logIndex >= 0 && logIndex < r.receivers.size()) {
- Object curReceiver = r.receivers.get(logIndex);
- if (curReceiver instanceof BroadcastFilter) {
- BroadcastFilter bf = (BroadcastFilter) curReceiver;
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
- bf.owningUserId, System.identityHashCode(r),
- r.intent.getAction(), logIndex, System.identityHashCode(bf));
- } else {
- ResolveInfo ri = (ResolveInfo) curReceiver;
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
- UserHandle.getUserId(ri.activityInfo.applicationInfo.uid),
- System.identityHashCode(r), r.intent.getAction(), logIndex, ri.toString());
- }
- } else {
- if (logIndex < 0) Slog.w(TAG,
- "Discarding broadcast before first receiver is invoked: " + r);
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
- -1, System.identityHashCode(r),
- r.intent.getAction(),
- r.nextReceiver,
- "NONE");
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- public void run() {
- mApp.appNotResponding(null, null, null, null, false, mAnnotation);
- }
mApp是ProcessRecord ,所有会调用ProcessRecord 的appNotResponding,这部分上一章有说过:深入理解 ANR 触发原理:一
所以对于Broadcast ANR问题算研究完毕。那么对于开始提的两个问题我们就回答了
答:1.所属的广播必须是有序广播,并行广播不会产生ANR问题。
2.发生有序广播,receivers>=2。如果只有一个那么也是不会产生ANR.
3 前台超时10s,后台超时60s,如果是多个receivers 那么总的时间不能超过 2 * mConstants.TIMEOUT * numReceivers
4.如果 有序广播是最后一个执行者,或者进程已经被杀掉。也不会产生ANR. if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
return;
}
5.或者在执行broadcastTimeoutLocked 方法是,receiver在执行finishReceiverLocked的方法。
答:1. 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);
2. 在event log 里面能搜索到 am_broadcast_discard_app ,是由logBroadcastReceiverDiscardLocked 方法得来。
3 .Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
+ ", started " + (now - r.receiverTime) + "ms ago");
4. Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
5. EventLog.writeEvent(EventLogTags.AM_ANR, userId, pid, processName, info.flags,
annotation); 这个log的输出在某些情况是不会输出的,用户定义了ANR的处理。还有就是另外几种情况,isShuttingDown ,isNotResponding ,isCrashing ,killedByAm,killed
6 .生成 data/anr/文件,里面保存了一些pid的trace 信息以及cpu信息。
7.DropBoxManager也会输出一些anr的trace信息。
8 .如果是前台进程还会有一个AppNotRespondingDialog。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。