android U广播详解(二)
android U广播详解(一)
基础代码介绍
广播相关
// 用作单个进程批量分发receivers,已被丢弃
frameworks/base/services/core/java/com/android/server/am/BroadcastReceiverBatch.java
// 主要逻辑所在类,包括入队、分发、结束等
frameworks/base/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
// 进程队列,分发广播时的单位
frameworks/base/services/core/java/com/android/server/am/BroadcastProcessQueue.java
// 之前的BroadcastQueue
frameworks/base/services/core/java/com/android/server/am/BroadcastQueueImpl.java
// 决定是否跳过分发当前receiver
frameworks/base/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
默认使用BroadcastQueueModernImpl类来处理广播相关逻辑:
// 默认使用modern Queue
if (mEnableModernQueue) {mBroadcastQueues = new BroadcastQueue[1];mBroadcastQueues[0] = new BroadcastQueueModernImpl(this, mHandler,foreConstants, backConstants);
} else {......
}
每个接收者的交付状态
// BroadcastRecord.java@IntDef(flag = false, prefix = { "DELIVERY_" }, value = {DELIVERY_PENDING, // 初始状态:等待未来运行DELIVERY_DELIVERED, // 终端状态:成功完成DELIVERY_SKIPPED, // 终端状态:由于内部政策而跳过DELIVERY_TIMEOUT, // 终端状态:尝试投递时超时DELIVERY_SCHEDULED, // 中间状态:当前正在执行DELIVERY_FAILURE, // 终端状态:派送失败})final @DeliveryState int[] delivery; // 每个接受者的交付状态// 获取指定index对应receiver的交付状态@DeliveryState int getDeliveryState(int index) {return delivery[index];}// 如果给定的交付状态为“终端”,则返回,其中不会进行额外的交付状态更改。static boolean isDeliveryStateTerminal(@DeliveryState int deliveryState) {switch (deliveryState) {case DELIVERY_DELIVERED:case DELIVERY_SKIPPED:case DELIVERY_TIMEOUT:case DELIVERY_FAILURE:return true;default:return false;}}// 如果给定的传递状态为“超出”,则返回,这意味着我们已经超出了该接收器,并且未来的接收器现在已解锁。static boolean isDeliveryStateBeyond(@DeliveryState int deliveryState) {switch (deliveryState) {case DELIVERY_DELIVERED:case DELIVERY_SKIPPED:case DELIVERY_TIMEOUT:case DELIVERY_FAILURE:case DELIVERY_DEFERRED:return true;default:return false;}// 更新给定索引的{@link #receivers}传递状态。,自动更新与状态变化相关的任何时间测量// 如果由于此状态转换{@link #beyondCount}而发生变化则返回true,表明其他事件可能会被解除阻塞。@CheckResultboolean setDeliveryState(int index, @DeliveryState int newDeliveryState,@NonNull String reason) {final int oldDeliveryState = delivery[index];if (isDeliveryStateTerminal(oldDeliveryState)|| newDeliveryState == oldDeliveryState) {// 我们已经到达终端或请求状态,因此请保留第一次转换时的所有统计数据和原因完整无缺return false;}switch (oldDeliveryState) {case DELIVERY_DEFERRED:deferredCount--;break;}switch (newDeliveryState) {case DELIVERY_PENDING:scheduledTime[index] = 0;break;case DELIVERY_SCHEDULED:scheduledTime[index] = SystemClock.uptimeMillis();break;case DELIVERY_DEFERRED:deferredCount++;break;case DELIVERY_DELIVERED:case DELIVERY_SKIPPED:case DELIVERY_TIMEOUT:case DELIVERY_FAILURE:terminalTime[index] = SystemClock.uptimeMillis();terminalCount++;break;}delivery[index] = newDeliveryState;deliveryReasons[index] = reason;// 如果当前receiver的状态变化可能会让我们达到一个新的beyondCountfinal int oldBeyondCount = beyondCount;if (index >= beyondCount) {for (int i = beyondCount; i < delivery.length; i++) {if (isDeliveryStateBeyond(getDeliveryState(i))) {beyondCount = i + 1;} else {break;}}}return (beyondCount != oldBeyondCount);}
广播种类
// BroadcastRecord.java// 紧急广播,关于此广播传送优先级的核心策略确定,前台、用户交互触发、闹钟触发boolean isUrgent() {return (isForeground()|| interactive|| alarm);}// 负载广播boolean isOffload() {return (intent.getFlags() & Intent.FLAG_RECEIVER_OFFLOAD) != 0;}// 确定 {@link #calculateBlockedUntilTerminalCount} 的结果是否对接收方进行了优先排序。@VisibleForTestingstatic boolean isPrioritized(@NonNull int[] blockedUntilBeyondCount,boolean ordered) {return !ordered && (blockedUntilBeyondCount.length > 0)&& (blockedUntilBeyondCount[0] != -1);}
// BroadcastProcessQueue.java
// 等待发送到此进程的广播的有序集合,作为一对 {@link BroadcastRecord} 和代表接收者的 {@link BroadcastRecord#receivers} 的索引。
private final ArrayDeque<SomeArgs> mPending = new ArrayDeque<>();// 等待分派到此进程的“紧急”广播的有序集合,与 {@link #mPending} 的表示相同。
private final ArrayDeque<SomeArgs> mPendingUrgent = new ArrayDeque<>(4);// 等待分派到此进程的“卸载”广播的有序集合,与{@link #mPending} 的表示相同。
private final ArrayDeque<SomeArgs> mPendingOffload = new ArrayDeque<>(4);
// 持有等待调度的广播的所有队列的列表
private final List<ArrayDeque<SomeArgs>> mPendingQueues = List.of(mPendingUrgent, mPending, mPendingOffload);
进程队列状态
可运行状态
// BroadcastProcessQueue.javaprivate @UptimeMillisLong long mRunnableAt = Long.MAX_VALUE;private @Reason int mRunnableAtReason = REASON_EMPTY;private boolean mRunnableAtInvalidated;// 未处于Blocked状态,且队列不为空public boolean isRunnable() {if (mRunnableAtInvalidated) updateRunnableAt();return mRunnableAt != Long.MAX_VALUE;}// 返回此进程被视为可运行的时间,通常是下一个pending广播的首次入队时间,也有可能是暂停或延迟时间public @UptimeMillisLong long getRunnableAt() {if (mRunnableAtInvalidated) updateRunnableAt();return mRunnableAt;}// 返回当前 {@link #getRunnableAt()} 值背后的“原因”,例如表明队列延迟或暂停的原因。public @Reason int getRunnableAtReason() {if (mRunnableAtInvalidated) updateRunnableAt();return mRunnableAtReason;}// 前一个receiver分发完,则应刷新下一个receiver所在队列的runnablepublic void invalidateRunnableAt() {mRunnableAtInvalidated = true;}// 快速确定此队列是否有等待传递给清单接收者的广播,这表明我们应该请求 OOM 调整。public boolean isPendingManifest() {return mCountManifest > 0;}
// BroadcastQueueModernImpl.java// 从 UID 映射到每个进程的广播队列。 如果一个 UID 托管多个进程,则每个额外的进程都使用 {@link BroadcastProcessQueue#next} 存储为链表。@GuardedBy("mService")private final SparseArray<BroadcastProcessQueue> mProcessQueues = new SparseArray<>();// 包含“可运行”队列的链表头。 它们按 {@link BroadcastProcessQueue#getRunnableAt()} 排序,因此我们更喜欢首先调度等待时间较长的广播。private BroadcastProcessQueue mRunnableHead = null;
运行中状态
// BroadcastProcessQueue.java// 正在调度的活动广播private @Nullable BroadcastRecord mActive;// 这是 {@link #mActive} 的 {@link BroadcastRecord#receivers} 列表的索引。private int mActiveIndex;public boolean isActive() {return mActive != null;}// 将当前活动的广播设置为下一个待处理的广播。public void makeActiveNextPending() {// TODO: what if the next broadcast isn't runnable yet?final SomeArgs next = removeNextBroadcast();mActive = (BroadcastRecord) next.arg1;mActiveIndex = next.argi1;mActiveCountSinceIdle++;mActiveViaColdStart = false;next.recycle();onBroadcastDequeued(mActive, mActiveIndex);}
// BroadcastQueueModernImpl.java// 当前正在“运行”的队列数组,可能有 {@code null} 的间隙。@GuardedBy("mService")private final BroadcastProcessQueue[] mRunning;// 正在“运行”但正在等待通过 {@link #onApplicationAttachedLocked} 完成冷启动的单个队列。 为了优化系统健康,我们一次只请求一个冷启动。@GuardedBy("mService")private @Nullable BroadcastProcessQueue mRunningColdStart;// 返回 {@link #mRunning} 中包含的活动队列总数。private int getRunningSize() {int size = 0;for (int i = 0; i < mRunning.length; i++) {if (mRunning[i] != null) size++;}return size;}
idle状态
// BroadcastProcessQueue.java // 快速确定此队列是否有仍在等待在未来某个时间点传送的广播。public boolean isIdle() {return !isActive() && isEmpty();}// 将当前运行的广播设置为空闲。public void makeActiveIdle() {mActive = null;mActiveIndex = 0;mActiveCountSinceIdle = 0;mActiveViaColdStart = false;invalidateRunnableAt();}
分发相关
有序广播分发阻塞
// BroadcastRecord.java// 已经接收完成/失败/Defer状态的receiver数量,即receiver状态不会影响后续receiver接收的数量
int beyondCount;// 根据整体广播的当前状态,是否应将给定的 {@link #receivers} 索引视为已Blocked。
boolean isBlocked(int index) {return (beyondCount < blockedUntilBeyondCount[index]);
}
计算每个接收器应被视为阻塞的 {@link #beyondCount} 。
例如,在有序广播中,接收器 {@code N} 被阻塞,直到接收器 {@code N-1} 达到终止状态。
同样,在一个优先级广播,接收者 {@code N} 被阻塞,直到所有更高优先级的接收者达到终止状态。
当没有终端计数约束时,每个接收者的阻塞值为 {@code -1}。
// BroadcastRecord.java
@VisibleForTesting
static @NonNull int[] calculateBlockedUntilBeyondCount(@NonNull List<Object> receivers, boolean ordered) {final int N = receivers.size();final int[] blockedUntilBeyondCount = new int[N];int lastPriority = 0;int lastPriorityIndex = 0;for (int i = 0; i < N; i++) {if (ordered) {// 当发送有序广播时,我们需要阻塞这个接收者,直到所有之前的接收者都终止blockedUntilBeyondCount[i] = i;} else {// 发送优先级广播时,我们只需要等待前一批接收者终止final int thisPriority = getReceiverPriority(receivers.get(i));if ((i == 0) || (thisPriority != lastPriority)) {lastPriority = thisPriority;lastPriorityIndex = i;blockedUntilBeyondCount[i] = i;} else {blockedUntilBeyondCount[i] = lastPriorityIndex;}}}// 如果整个列表都在同一个优先级中,标记为-1表示它们都不需要等待if (N > 0 && blockedUntilBeyondCount[N - 1] == 0) {Arrays.fill(blockedUntilBeyondCount, -1);}return blockedUntilBeyondCount;
}
选取队列中下一个待执行广播
// BroadcastProcessQueue.java// 自上次非紧急调度以来已调度的连续紧急广播数。
private int mActiveCountConsecutiveUrgent;// 自上次负载调度以来已调度的连续正常广播数。
private int mActiveCountConsecutiveNormal;
// BroadcastProcessQueue.java// 将当前活动的广播设置为下一个待处理的广播。public void makeActiveNextPending() {// TODO: what if the next broadcast isn't runnable yet?final SomeArgs next = removeNextBroadcast();mActive = (BroadcastRecord) next.arg1;mActiveIndex = next.argi1;mActiveCountSinceIdle++;mActiveAssumedDeliveryCountSinceIdle +=(mActive.isAssumedDelivered(mActiveIndex) ? 1 : 0);mActiveViaColdStart = false;mActiveWasStopped = false;next.recycle();onBroadcastDequeued(mActive, mActiveIndex);}@Nullable ArrayDeque<SomeArgs> queueForNextBroadcast() {// 普通优先级大于负载,但是限制为10final ArrayDeque<SomeArgs> nextNormal = queueForNextBroadcast(mPending, mPendingOffload,mActiveCountConsecutiveNormal, constants.MAX_CONSECUTIVE_NORMAL_DISPATCHES); //10// 紧急优先级大于普通,限制为3final ArrayDeque<SomeArgs> nextBroadcastQueue = queueForNextBroadcast(mPendingUrgent, nextNormal,mActiveCountConsecutiveUrgent, constants.MAX_CONSECUTIVE_URGENT_DISPATCHES); //3return nextBroadcastQueue;}private @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast(@Nullable ArrayDeque<SomeArgs> highPriorityQueue,@Nullable ArrayDeque<SomeArgs> lowPriorityQueue,int consecutiveHighPriorityCount, // 连续高优先级计数int maxHighPriorityDispatchLimit) { // 最大高优先级调度限制 10 or 3// 没有高优先级pending,没有进一步的决策if (isQueueEmpty(highPriorityQueue)) {return lowPriorityQueue;}// 只有高优先级pending,也没有进一步的决策if (isQueueEmpty(lowPriorityQueue)) {return highPriorityQueue;}// 缓解饥饿:虽然我们默认优先考虑高优先级队列,但我们允许低优先级队列稳定前进,即使高优先级队列中的广播到达速度快于它们的调度速度。// 我们不会尝试推迟到低优先级队列中的下一个广播,如果该广播是有序的并且在传递给其他receiver时仍然被阻止。final SomeArgs nextLPArgs = lowPriorityQueue.peekFirst();final BroadcastRecord nextLPRecord = (BroadcastRecord) nextLPArgs.arg1;final int nextLPRecordIndex = nextLPArgs.argi1;final BroadcastRecord nextHPRecord = (BroadcastRecord) highPriorityQueue.peekFirst().arg1;final boolean shouldConsiderLPQueue = (mCountPrioritizeEarliestRequests // 最早优先,waitFor// 连续高优先级处理达到阈值3 or 10|| consecutiveHighPriorityCount >= maxHighPriorityDispatchLimit);// 低优先级队列符合条件final boolean isLPQueueEligible = shouldConsiderLPQueue// 低优先级队列入队时间更早&& nextLPRecord.enqueueTime <= nextHPRecord.enqueueTime// 低优先级队列未处于有序广播分发的阻塞状态&& !nextLPRecord.isBlocked(nextLPRecordIndex);return isLPQueueEligible ? lowPriorityQueue : highPriorityQueue;}
队列是否继续调度
// BroadcastProcessQueue.java// 自此队列上次空闲以来已分派的 {@link #mActive} 广播计数,会在shouldContinueScheduling被使用,一个进程一次最多可分发8 or 16个广播private int mActiveCountSinceIdle;public int getActiveCountSinceIdle() {return mActiveCountSinceIdle;}// 如果我们知道此队列正在运行的“热”进程,则返回。public boolean isProcessWarm() {return (app != null) && (app.getOnewayThread() != null) && !app.isKilled();}
// BroadcastQueueModernImpl.java// 如果队列中不再有广播或者队列不可运行,则返回 true。private boolean shouldRetire(@NonNull BroadcastProcessQueue queue) {// 如果我们已经取得了合理的进展,请定期退出,以避免其他进程饥饿和广播立即完成而无需等待时堆栈溢出final boolean shouldRetire;if (UserHandle.isCore(queue.uid)) {// 动态注册的无序广播数量final int nonBlockingDeliveryCount = queue.getActiveAssumedDeliveryCountSinceIdle();// 有序广播 & 清单注册的广播数量final int blockingDeliveryCount = (queue.getActiveCountSinceIdle()- queue.getActiveAssumedDeliveryCountSinceIdle());shouldRetire = (blockingDeliveryCount// 16(低内存8)>= mConstants.MAX_CORE_RUNNING_BLOCKING_BROADCASTS) || (nonBlockingDeliveryCount// 64 (低内存32)>= mConstants.MAX_CORE_RUNNING_NON_BLOCKING_BROADCASTS);} else {// 默认16(低内存8),系统可配置shouldRetire =(queue.getActiveCountSinceIdle() >= mConstants.MAX_RUNNING_ACTIVE_BROADCASTS);}return !queue.isRunnable() || !queue.isProcessWarm() || shouldRetire;}
整体流程
入队流程:
冷启会多如下流程:
分发流程:
静态注册或有序广播结束分发后回调到AMS的流程:
入队
// BroadcastQueueModernImpl.java@Overridepublic void enqueueBroadcastLocked(@NonNull BroadcastRecord r) {if (DEBUG_BROADCAST) logv("Enqueuing " + r + " for " + r.receivers.size() + " receivers");final int cookie = traceBegin("enqueueBroadcast");// 通过将它们重新定位到 {@link UserHandle#USER_SYSTEM},对单例进程托管的清单接收器进行特殊处理。r.applySingletonPolicy(mService);// 下发组策略,遍历所有队列决定之前的广播是否跳过分发或合并分发applyDeliveryGroupPolicy(r);// 设置入队时间r.enqueueTime = SystemClock.uptimeMillis();r.enqueueRealTime = SystemClock.elapsedRealtime();r.enqueueClockTime = System.currentTimeMillis();mHistory.onBroadcastEnqueuedLocked(r);ArraySet<BroadcastRecord> replacedBroadcasts = mReplacedBroadcastsCache.getAndSet(null);if (replacedBroadcasts == null) {replacedBroadcasts = new ArraySet<>();}boolean enqueuedBroadcast = false;for (int i = 0; i < r.receivers.size(); i++) {final Object receiver = r.receivers.get(i);final BroadcastProcessQueue queue = getOrCreateProcessQueue(getReceiverProcessName(receiver), getReceiverUid(receiver));// 如果要跳过此接收器,请立即跳过它,甚至不要将其加入队列。// 诸如一些权限校验等final String skipReason = mSkipPolicy.shouldSkipMessage(r, receiver);if (skipReason != null) {setDeliveryState(null, null, r, i, receiver, BroadcastRecord.DELIVERY_SKIPPED,"skipped by policy at enqueue: " + skipReason);continue;}// 入队对应的进程队列enqueuedBroadcast = true;final BroadcastRecord replacedBroadcast = queue.enqueueOrReplaceBroadcast(r, i, mBroadcastConsumerDeferApply);if (replacedBroadcast != null) {replacedBroadcasts.add(replacedBroadcast);}// 更新可运行时间updateRunnableList(queue);// schedule 运行队列enqueueUpdateRunningList();}// 跳过任何已被带有 FLAG_RECEIVER_REPLACE_PENDING 的新广播取代的广播。skipAndCancelReplacedBroadcasts(replacedBroadcasts);replacedBroadcasts.clear();// If nothing to dispatch, send any pending result immediatelyif (r.receivers.isEmpty()) {scheduleResultTo(r);notifyFinishBroadcast(r);}traceEnd(cookie);}
应用对应的传递组策略
// BroadcastQueueModernImpl.javaprivate void applyDeliveryGroupPolicy(@NonNull BroadcastRecord r) {if (mService.shouldIgnoreDeliveryGroupPolicy(r.intent.getAction())) {return;}// 发送广播时可以指定下发策略final int policy = (r.options != null)? r.options.getDeliveryGroupPolicy() : BroadcastOptions.DELIVERY_GROUP_POLICY_ALL;final BroadcastConsumer broadcastConsumer;switch (policy) {case BroadcastOptions.DELIVERY_GROUP_POLICY_ALL:// 在这种情况下,需要保留较旧的广播,因此无事可做。return;case BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT:// 匹配上则直接skip当前派发broadcastConsumer = mBroadcastConsumerSkipAndCanceled;break;case BroadcastOptions.DELIVERY_GROUP_POLICY_MERGED:final BundleMerger extrasMerger = r.options.getDeliveryGroupExtrasMerger();if (extrasMerger == null) {// Extras合并需要能够合并extras。 因此,如果未提供,则忽略交付组策略。return;}// 先合并额外的数据,再跳过分发broadcastConsumer = (record, recordIndex) -> {r.intent.mergeExtras(record.intent, extrasMerger);mBroadcastConsumerSkipAndCanceled.accept(record, recordIndex);};break;default:logw("Unknown delivery group policy: " + policy);return;}final ArrayMap<BroadcastRecord, Boolean> recordsLookupCache = getRecordsLookupCache();forEachMatchingBroadcast(QUEUE_PREDICATE_ANY, (testRecord, testIndex) -> {// 如果接收方已经处于终端状态,则忽略它。if (isDeliveryStateTerminal(testRecord.getDeliveryState(testIndex))) {return false;}// 我们只允许调用者删除他们排队的广播if ((r.callingUid != testRecord.callingUid)|| (r.userId != testRecord.userId)|| !r.matchesDeliveryGroup(testRecord)) {return false;}// 对于有序广播,请检查新广播的接收器是否是前一个广播的接收器的超集,// 因为跳过和仅删除其中一个可能会导致不一致的状态。if (testRecord.ordered || testRecord.prioritized) {return containsAllReceivers(r, testRecord, recordsLookupCache);} else if (testRecord.resultTo != null) {return testRecord.getDeliveryState(testIndex) == DELIVERY_DEFERRED? r.containsReceiver(testRecord.receivers.get(testIndex)): containsAllReceivers(r, testRecord, recordsLookupCache);} else {return r.containsReceiver(testRecord.receivers.get(testIndex));}}, broadcastConsumer, true);recordsLookupCache.clear();mRecordsLookupCache.compareAndSet(null, recordsLookupCache);}// 典型的消费者会跳过给定的广播并将其标记为已取消,通常是因为它与predicate匹配。private final BroadcastConsumer mBroadcastConsumerSkipAndCanceled = (r, i) -> {setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_SKIPPED,"mBroadcastConsumerSkipAndCanceled");r.resultCode = Activity.RESULT_CANCELED;r.resultData = null;r.resultExtras = null;};
添加广播到对应的进程队列
将给定的广播排队,以便在未来的某个时间点发送到此进程。 目标接收器由 {@link BroadcastRecord#receivers} 中的给定索引指示。
如果广播被标记为 {@link BroadcastRecord#isReplacePending()},则此调用将替换任何待处理的调度; 否则它将作为普通广播排队。
定义后,此接收器被视为“阻塞”,直到至少给定计数的其他接收器达到终止状态; 通常用于有序广播和优先级广播。
// BroadcastProcessQueue.javapublic BroadcastRecord enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record, int recordIndex,@NonNull BroadcastConsumer replacedBroadcastConsumer) {// 当 updateDeferredStates() 已将延迟状态应用于所有待处理项目时,也应用于此新广播if (mLastDeferredStates && record.deferUntilActive&& (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) {deferredStatesApplyConsumer.accept(record, recordIndex);}// 如果发件人使用 BroadcastOptions 交付组 API 指定了策略,则忽略 FLAG_RECEIVER_REPLACE_PENDING。if (record.isReplacePending()&& record.getDeliveryGroupPolicy() == BroadcastOptions.DELIVERY_GROUP_POLICY_ALL) {final BroadcastRecord replacedBroadcastRecord = replaceBroadcast(record, recordIndex);if (replacedBroadcastRecord != null) {return replacedBroadcastRecord;}}// 调用者对替换不感兴趣,或者我们没有在上面找到任何待替换的项目,因此作为新广播排队SomeArgs newBroadcastArgs = SomeArgs.obtain();newBroadcastArgs.arg1 = record;newBroadcastArgs.argi1 = recordIndex;// 交叉广播优先级策略:一些广播可能保证在其他已经pending的广播之前发布,例如,如果这个新广播处于不同的交付类别或与具有隐式响应期望的直接用户交互相关联。getQueueForBroadcast(record).addLast(newBroadcastArgs);onBroadcastEnqueued(record, recordIndex);return null;}// 判断广播类别,紧急、负载、普通private @NonNull ArrayDeque<SomeArgs> getQueueForBroadcast(@NonNull BroadcastRecord record) {// 前台、用户交互、闹钟if (record.isUrgent()) {return mPendingUrgent;} else if (record.isOffload()) {return mPendingOffload;} else {return mPending;}}
当给定的记录已入队时更新摘要统计信息,跟onBroadcastDequeued对应。
// BroadcastProcessQueue.javaprivate void onBroadcastEnqueued(@NonNull BroadcastRecord record, int recordIndex) {if (record.deferUntilActive) {mCountDeferred++;}if (record.isForeground()) {if (record.deferUntilActive) {mCountForegroundDeferred++;}mCountForeground++;}if (record.ordered) {mCountOrdered++;}if (record.alarm) {mCountAlarm++;}if (record.prioritized) {if (record.deferUntilActive) {mCountPrioritizedDeferred++;}mCountPrioritized++;}if (record.interactive) {mCountInteractive++;}if (record.resultTo != null) {mCountResultTo++;}if (record.callerInstrumented) {mCountInstrumented++;}if (record.receivers.get(recordIndex) instanceof ResolveInfo) {mCountManifest++;}invalidateRunnableAt();}
更新当前队列的RunnbaleAt
- 默认mRunnableAt时间为广播的入队时间+500ms
- 当前队列中有有序、闹钟、优先级、receiver为manifest或当前是persistent进程等,mRunnableAt时间为广播的入队时间。
- 当前进程队列有前台广播、源于用户交互广播、由由root/shell/active instrument发送广播或当前进程为instrumented或当前app为top app,则mRunnableAt为入队时间-120s
- 当前进程处于冻结状态,mRunnableAt为入队时间+120s或者defer
// BroadcastProcessQueue.javaprivate void updateRunnableAt() {if (!mRunnableAtInvalidated) return;mRunnableAtInvalidated = false;// 获取下一个待处理的广播final SomeArgs next = peekNextBroadcast();if (next != null) {final BroadcastRecord r = (BroadcastRecord) next.arg1;final int index = next.argi1;final long runnableAt = r.enqueueTime;// 如果我们特别排在其他有序调度活动之后,我们还不能运行if (r.isBlocked(index)) {mRunnableAt = Long.MAX_VALUE;mRunnableAtReason = REASON_BLOCKED;return;}// 对此进程的任何广播需要延迟的持续时间if (mForcedDelayedDurationMs > 0) {mRunnableAt = runnableAt + mForcedDelayedDurationMs;mRunnableAtReason = REASON_FORCE_DELAYED;// 前台广播数量 > mCountForegroundDeferred} else if (mCountForeground > mCountForegroundDeferred) {mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS; //-120smRunnableAtReason = REASON_CONTAINS_FOREGROUND;// 源于用户交互广播数量 > 0} else if (mCountInteractive > 0) {mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS; //-120smRunnableAtReason = REASON_CONTAINS_INTERACTIVE;// 广播由root/shell/active instrument发送数量 > 0} else if (mCountInstrumented > 0) {mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS; //-120smRunnableAtReason = REASON_CONTAINS_INSTRUMENTED;// 当前进程处于instrumented} else if (mProcessInstrumented) {mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS; //-120smRunnableAtReason = REASON_INSTRUMENTED;// PROCESS_STATE_TOP top app} else if (mUidForeground) {mRunnableAt = runnableAt + constants.DELAY_FOREGROUND_PROC_MILLIS; //-120smRunnableAtReason = REASON_FOREGROUND;} else if (app != null && app.getSetProcState() == ActivityManager.PROCESS_STATE_TOP) {// TODO (b/287676625): Use a callback to check when a process goes in and out of// the TOP state.mRunnableAt = runnableAt + constants.DELAY_FOREGROUND_PROC_MILLIS;mRunnableAtReason = REASON_TOP_PROCESS;} else if (mProcessPersistent) {mRunnableAt = runnableAt + constants.DELAY_PERSISTENT_PROC_MILLIS;mRunnableAtReason = REASON_PERSISTENT;} else if (UserHandle.isCore(uid)) {mRunnableAt = runnableAt;mRunnableAtReason = REASON_CORE_UID;// 有序广播数量 > 0 else if (mCountOrdered > 0) {mRunnableAt = runnableAt;mRunnableAtReason = REASON_CONTAINS_ORDERED;// 闹钟触发的广播 > 0} else if (mCountAlarm > 0) {mRunnableAt = runnableAt;mRunnableAtReason = REASON_CONTAINS_ALARM;// 优先级广播数量 > mCountPrioritizedDeferred} else if (mCountPrioritized > mCountPrioritizedDeferred) {mRunnableAt = runnableAt;mRunnableAtReason = REASON_CONTAINS_PRIORITIZED;// 有final receiver的广播 > mCountPrioritizedDeferred} else if (mCountManifest > 0) {mRunnableAt = runnableAt;mRunnableAtReason = REASON_CONTAINS_MANIFEST;// 进程冻结} else if (mProcessFreezable) {// 广播有设置deferUntilActiveif (r.deferUntilActive) {// 所有排队的广播都是可推迟的,deferif (mCountDeferred == mCountEnqueued) {mRunnableAt = Long.MAX_VALUE;mRunnableAtReason = REASON_CACHED_INFINITE_DEFER;} else {// 至少有一个排队的广播不可推迟,请重新选择此记录的时间和原因。 // 如果后来的记录不可推迟并且是这些特殊情况之一,则上述情况之一已经捕获了这一点。if (r.isForeground()) {mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;mRunnableAtReason = REASON_CONTAINS_FOREGROUND;} else if (r.prioritized) {mRunnableAt = runnableAt;mRunnableAtReason = REASON_CONTAINS_PRIORITIZED;} else if (r.resultTo != null) {mRunnableAt = runnableAt;mRunnableAtReason = REASON_CONTAINS_RESULT_TO;} else {mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS; //120smRunnableAtReason = REASON_CACHED;}}} else {// This record isn't deferrablemRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;// 120smRunnableAtReason = REASON_CACHED;}} else if (mCountResultTo > 0) {mRunnableAt = runnableAt;mRunnableAtReason = REASON_CONTAINS_RESULT_TO;// 当前进程的为manifest的receiver数量 > 0} else {mRunnableAt = runnableAt + constants.DELAY_NORMAL_MILLIS; // +500msmRunnableAtReason = REASON_NORMAL;}// 如果我们有太多待处理的广播,绕过上面可能应用的任何延迟以帮助耗尽if (mPending.size() + mPendingUrgent.size()+ mPendingOffload.size() >= constants.MAX_PENDING_BROADCASTS) {mRunnableAt = Math.min(mRunnableAt, runnableAt);mRunnableAtReason = REASON_MAX_PENDING;}} else {mRunnableAt = Long.MAX_VALUE;mRunnableAtReason = REASON_EMPTY;}}
更新可运行队列
考虑更新“可运行”队列的列表,特别是与给定队列相关的列表。
通常在 {@link BroadcastProcessQueue#getRunnableAt()} 可能已更改时调用,因为这会影响我们将“可运行”队列提升为“正在运行”的顺序。
// BroadcastQueueModernImpl.java@GuardedBy("mService")private void updateRunnableList(@NonNull BroadcastProcessQueue queue) {if (getRunningIndexOf(queue) >= 0) {// 已经运行; 一旦它们运行完毕,它们将被重新插入到可运行列表中,所以现在不需要更新它们return;}// 为了将自己正确地放入可运行列表中,我们可能需要更新可能已失效的内部结构; // 我们在最后一刻才等到,以避免重复工作queue.updateDeferredStates(mBroadcastConsumerDeferApply, mBroadcastConsumerDeferClear);queue.updateRunnableAt();// 是可运行状态final boolean wantQueue = queue.isRunnable();// 已在队列中final boolean inQueue = (queue == mRunnableHead) || (queue.runnableAtPrev != null)|| (queue.runnableAtNext != null);if (wantQueue) {if (inQueue) {// 我们处于队列中,但我们在链表中的位置可能需要根据 // runnableAt 更改进行移动,链表前面的额队列总是应先被分发final boolean prevLower = (queue.runnableAtPrev != null)? queue.runnableAtPrev.getRunnableAt() <= queue.getRunnableAt() : true;final boolean nextHigher = (queue.runnableAtNext != null)? queue.runnableAtNext.getRunnableAt() >= queue.getRunnableAt() : true;if (!prevLower || !nextHigher) {mRunnableHead = removeFromRunnableList(mRunnableHead, queue);mRunnableHead = insertIntoRunnableList(mRunnableHead, queue);}} else {mRunnableHead = insertIntoRunnableList(mRunnableHead, queue);}// 从队列中移除} else if (inQueue) {mRunnableHead = removeFromRunnableList(mRunnableHead, queue);}// 当前队列为空、不是正在调度、进程不存活等,移除该队列if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) {removeProcessQueue(queue.processName, queue.uid);}}
分发
更新正在运行队列
考虑更新“正在运行”队列的列表。 此方法可以将“可运行”队列提升为“正在运行”,受限于最多 {@link BroadcastConstants#MAX_RUNNING_PROCESS_QUEUES}(2 or 4) 热进程和只有一个挂起的冷启动。
一次可运行的队列数量为MAX_RUNNING_PROCESS_QUEUES(低内存2个,否则4个)+EXTRA_RUNNING_URGENT_PROCESS_QUEUES(1个)
- 如果当前正在运行的列表>= (2 or 4),则取紧急的队列
- 当前队列未到执行时间,则在指定时间再次执行更新
- 当前队列的进程是冷启动,但是已经有一个需要冷启动的队列正在运行,则取下一个队列
// BroadcastQueueModernImpl.java@GuardedBy("mService")private void updateRunningListLocked() {// 此处分配的大小隐含地包括超出正常并行性的 MAX_RUNNING_QUEUES 软限制的紧急调度的额外预留。// 如果我们已经在调度一些紧急广播,请先将其与额外广播放在一起 - 它的作用是在正常预留完全被不太紧急的调度占用时允许紧急广播流量的进展,而不是通常扩展并行性。final int usedExtra = Math.min(getRunningUrgentCount(),mConstants.EXTRA_RUNNING_URGENT_PROCESS_QUEUES); //1 // 空闲数量int avail = mRunning.length - getRunningSize() - usedExtra;if (avail == 0) return;final int cookie = traceBegin("updateRunningList");final long now = SystemClock.uptimeMillis();// 如果有人正在等待一个状态,那么现在一切都可以运行final boolean waitingFor = !mWaitingFor.isEmpty();// 我们现在正在进行更新,因此删除任何未来的更新请求;// 如果需要,我们会在下面重新发布mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);boolean updateOomAdj = false;BroadcastProcessQueue queue = mRunnableHead;while (queue != null && avail > 0) {BroadcastProcessQueue nextQueue = queue.runnableAtNext;final long runnableAt = queue.getRunnableAt();// 当广播在列表遍历过程中被跳过或失败时,我们可能会遇到一个不再可运行的队列; 跳过它if (!queue.isRunnable()) {queue = nextQueue;continue;}// 如果我们达到了非紧急调度并行度的软限制if (getRunningSize() >= mConstants.MAX_RUNNING_PROCESS_QUEUES) { // 2 or 4 // 则只考虑从就绪广播为紧急的队列进行交付if (!queue.isPendingUrgent()) {queue = nextQueue;continue;}}// 最早的一个队列运行时间超出当前时间,跳出循环,并post 延时消息以便及时分发if (runnableAt > now && !waitingFor) {mLocalHandler.sendEmptyMessageAtTime(MSG_UPDATE_RUNNING_LIST, runnableAt);break;}// 我们可能还没有听说过新运行的进程,所以如果我们是冷启,请考虑刷新updateWarmProcess(queue);final boolean processWarm = queue.isProcessWarm();if (processWarm) {mService.mOomAdjuster.unfreezeTemporarily(queue.app,CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);// 该进程可能会作为解冻的一部分被终止。 所以,再检查一下是否仍然存在。if (!queue.isProcessWarm()) {queue = nextQueue;enqueueUpdateRunningList();continue;}} else {// 我们只提供一次运行一个冷启动以节省系统资源;// 下面我们要么声明单个插槽,要么跳到寻找另一个warm进程if (mRunningColdStart == null) {mRunningColdStart = queue;} else if (isPendingColdStartValid()) {// 转向考虑下一个可运行队列queue = nextQueue;continue;} else {// 挂起的冷启动无效,因此请清除它并继续。clearInvalidPendingColdStart();mRunningColdStart = queue;}}if (DEBUG_BROADCAST) logv("Promoting " + queue+ " from runnable to running; process is " + queue.app);promoteToRunningLocked(queue);boolean completed;if (processWarm) {updateOomAdj |= queue.runningOomAdjusted;try {completed = scheduleReceiverWarmLocked(queue);} catch (BroadcastDeliveryFailedException e) {reEnqueueActiveBroadcast(queue);completed = true;}} else {completed = scheduleReceiverColdLocked(queue);}// 如果我们完成了向进程传递广播,我们可以将其从“运行”列表中降级。if (completed) {demoteFromRunningLocked(queue);}// 可运行数量-1avail--;// Move to considering next runnable queuequeue = nextQueue;}// 更新adjif (updateOomAdj) {mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);}checkPendingColdStartValidity();checkAndRemoveWaitingFor();traceEnd(cookie);}
将进程提升到“正在运行”列表
// BroadcastQueueModernImpl.java@GuardedBy("mService")private void promoteToRunningLocked(@NonNull BroadcastProcessQueue queue) {// 分配此可用许可证并开始运行!final int queueIndex = getRunningIndexOf(null);// 将当前队列放在可运行数组中mRunning[queueIndex] = queue;// 从可运行链表中删除当前队列mRunnableHead = removeFromRunnableList(mRunnableHead, queue);// 将此进程的所有跟踪事件发送到一致的轨道中queue.runningTraceTrackName = TAG + ".mRunning[" + queueIndex + "]";// 指示此过程是否应进行 OOM 调整的标志,定义为升级到正在运行的插槽的一部分queue.runningOomAdjusted = queue.isPendingManifest()|| queue.isPendingOrdered()|| queue.isPendingResultTo();// 如果已经处于热启动,我们可以立即提出OOM调整请求; 否则我们需要等到进程变暖final boolean processWarm = queue.isProcessWarm();if (processWarm) {notifyStartedRunning(queue);}// 如果我们已经暖和了,现在就安排下一个待定的广播; 否则我们将等待冷启动回来// 设置队列的下一个active广播queue.makeActiveNextPending();if (processWarm) {queue.traceProcessRunningBegin();} else {queue.traceProcessStartingBegin();}}// 通知操作系统的其他部分给定的广播队列已开始运行,通常用于内部簿记。private void notifyStartedRunning(@NonNull BroadcastProcessQueue queue) {if (queue.app != null) {// 当前进程的ProcessReceiverRecord计数++queue.app.mReceivers.incrementCurReceivers();// 如果它在后台受限,不要改变它的 LRU 位置。.if (mService.mInternal.getRestrictionLevel(queue.uid) < ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET) {mService.updateLruProcessLocked(queue.app, false, null);}mService.mOomAdjuster.unfreezeTemporarily(queue.app,CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);// manifest receiver/有序/有最终接受者所在进程需要将procState提升到11if (queue.runningOomAdjusted) {queue.app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);mService.enqueueOomAdjTargetLocked(queue.app);}}}
将进程从“正在运行”列表中降级
// BroadcastQueueModernImpl.java@GuardedBy("mService")private void demoteFromRunningLocked(@NonNull BroadcastProcessQueue queue) {if (!queue.isActive()) {logw("Ignoring demoteFromRunning; no active broadcast for " + queue);return;}final int cookie = traceBegin("demoteFromRunning");// 我们已经耗尽了正在运行的广播; 也许回到可运行状态queue.makeActiveIdle();queue.traceProcessEnd();final int queueIndex = getRunningIndexOf(queue);mRunning[queueIndex] = null;// 更新可运行列表和Running列表,尝试进行下一个进程队列的分发updateRunnableList(queue);enqueueUpdateRunningList();// 告诉其他操作系统组件应用程序没有主动运行,从而有机会更新 OOM 调整notifyStoppedRunning(queue);traceEnd(cookie);}
热启分发
当我们知道进程处于热状态时,在给定队列上安排当前活动的广播。
无论是在远程应用程序处理广播的情况下,还是在没有远程应用程序的情况下在本地完成广播的情况下,都强烈希望通过调用 {@link #finishReceiverLocked} 来始终如一地处理所有涉及结果 。
@CheckResult@GuardedBy("mService")private boolean scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue)throws BroadcastDeliveryFailedException {// 检查状态是否是activecheckState(queue.isActive(), "isActive");final int cookie = traceBegin("scheduleReceiverWarmLocked");// 有序广播或静态注仅执行一次循环while (queue.isActive()) {final BroadcastRecord r = queue.getActive();final int index = queue.getActiveIndex();// 标记分发时间if (r.terminalCount == 0) {r.dispatchTime = SystemClock.uptimeMillis();r.dispatchRealTime = SystemClock.elapsedRealtime();r.dispatchClockTime = System.currentTimeMillis();}// 是否应该跳过分发final String skipReason = shouldSkipReceiver(queue, r, index);// 正常分发if (skipReason == null) {final boolean isBlockingDispatch = dispatchReceivers(queue, r, index);if (isBlockingDispatch) {traceEnd(cookie);return false;}} else {// 跳过则标记结束,状态为DELIVERY_SKIPPEDfinishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED, skipReason);}// 是否应该退出分发循环,达到数量限制会退出if (shouldRetire(queue)) {break;}// 标记下一个活动广播queue.makeActiveNextPending();}traceEnd(cookie);return true;}
冷启分发
当我们知道进程处于冷状态时,在给定队列上安排当前活动的广播。 这将导致冷启动,并最终会在准备就绪后调用 {@link #scheduleReceiverWarmLocked}。
// BroadcastQueueModernImpl.java@CheckResult@GuardedBy("mService")private boolean scheduleReceiverColdLocked(@NonNull BroadcastProcessQueue queue) {checkState(queue.isActive(), "isActive");// 标记活动广播是通过冷启动安排的queue.setActiveViaColdStart(true);final BroadcastRecord r = queue.getActive();final int index = queue.getActiveIndex();final Object receiver = r.receivers.get(index);// 忽略通过动态注册的receiver来冷启动if (receiver instanceof BroadcastFilter) {mRunningColdStart = null;finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED,"BroadcastFilter for cold app");return true;}// 判断是否需要跳过此次分发final String skipReason = shouldSkipReceiver(queue, r, index);if (skipReason != null) {mRunningColdStart = null;finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED, skipReason);return true;}final ApplicationInfo info = ((ResolveInfo) receiver).activityInfo.applicationInfo;final ComponentName component = ((ResolveInfo) receiver).activityInfo.getComponentName();if ((info.flags & ApplicationInfo.FLAG_STOPPED) != 0) {queue.setActiveWasStopped(true);}final int intentFlags = r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND;final HostingRecord hostingRecord = new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST,component, r.intent.getAction(), r.getHostingRecordTriggerType());final boolean isActivityCapable = (r.options != null&& r.options.getTemporaryAppAllowlistDuration() > 0);final int zygotePolicyFlags = isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE: ZYGOTE_POLICY_FLAG_EMPTY;final boolean allowWhileBooting = (r.intent.getFlags()& Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0;if (DEBUG_BROADCAST) logv("Scheduling " + r + " to cold " + queue);queue.app = mService.startProcessLocked(queue.processName, info, true, intentFlags,hostingRecord, zygotePolicyFlags, allowWhileBooting, false);// 启动进程失败if (queue.app == null) {mRunningColdStart = null;finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,"startProcessLocked failed");return true;}return false;}
冷启进程启动后走跟热启一样的流程
// BroadcastQueueModernImpl.java@Overridepublic boolean onApplicationAttachedLocked(@NonNull ProcessRecord app)throws BroadcastDeliveryFailedException {if (DEBUG_BROADCAST) {logv("Process " + app + " is attached");}// 进程记录可以被回收,所以总是从查找相关的每个进程队列开始final BroadcastProcessQueue queue = getProcessQueue(app);if (queue != null) {setQueueProcess(queue, app);}boolean didSomething = false;if ((mRunningColdStart != null) && (mRunningColdStart == queue)) {// 我们一直在等待这个应用程序冷启动,现在已经准备好了; 调度它的下一个广播并清除插槽mRunningColdStart = null;// 现在我们已经启动,我们终于可以请求我们一直在等待的 OOM 调整notifyStartedRunning(queue);mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);queue.traceProcessEnd();queue.traceProcessRunningBegin();try {// 跟热启流程一样了if (scheduleReceiverWarmLocked(queue)) {// 当前进程分发完成,从可运行列表中移除demoteFromRunningLocked(queue);}} catch (BroadcastDeliveryFailedException e) {reEnqueueActiveBroadcast(queue);demoteFromRunningLocked(queue);throw e;}// 我们可能愿意开始另一个冷启动,在ActivityManager线程中触发enqueueUpdateRunningList();didSomething = true;}return didSomething;}
分发到App进程
接收者即将被发送。 如有必要,启动 ANR 计时器。
如果需要等待finishReceiver回调,则返回true
// BroadcastQueueModernImpl.java
@CheckResult
private boolean dispatchReceivers(@NonNull BroadcastProcessQueue queue,@NonNull BroadcastRecord r, int index) throws BroadcastDeliveryFailedException {final ProcessRecord app = queue.app;final Object receiver = r.receivers.get(index);// 当有请求时或当我们立即假设交付成功时,在启动期间尽早跳过 ANR 跟踪final boolean assumeDelivered = r.isAssumedDelivered(index);if (mService.mProcessesReady && !r.timeoutExempt && !assumeDelivered) {queue.lastCpuDelayTime = queue.app.getCpuDelayTime();// 前台10s,后台60sfinal int softTimeoutMillis = (int) (r.isForeground() ? mFgConstants.TIMEOUT: mBgConstants.TIMEOUT);mLocalHandler.sendMessageDelayed(Message.obtain(mLocalHandler,MSG_DELIVERY_TIMEOUT_SOFT, softTimeoutMillis, 0, queue), softTimeoutMillis);}// 添加或更新后台启动权限if (r.mBackgroundStartPrivileges.allowsAny()) {app.addOrUpdateBackgroundStartPrivileges(r, r.mBackgroundStartPrivileges);// 10s豁免final long timeout = r.isForeground() ? mFgConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT: mBgConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT;final SomeArgs args = SomeArgs.obtain();args.arg1 = app;args.arg2 = r;mLocalHandler.sendMessageDelayed(Message.obtain(mLocalHandler, MSG_BG_ACTIVITY_START_TIMEOUT, args), timeout);}if (r.options != null && r.options.getTemporaryAppAllowlistDuration() > 0) {if (r.options.getTemporaryAppAllowlistType()== PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {// Only delay freezer, don't add to any temp allowlist// TODO: Add a unit testmService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER,r.options.getTemporaryAppAllowlistDuration());} else {// 临时白名单相关mService.tempAllowlistUidLocked(queue.uid,r.options.getTemporaryAppAllowlistDuration(),r.options.getTemporaryAppAllowlistReasonCode(), r.toShortString(),r.options.getTemporaryAppAllowlistType(), r.callingUid);}}if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);// 设置传递状态为SCHEDULEDsetDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED,"scheduleReceiverWarmLocked");final Intent receiverIntent = r.getReceiverIntent(receiver);final IApplicationThread thread = app.getOnewayThread();if (thread != null) {try {if (r.shareIdentity) {mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent,UserHandle.getAppId(app.uid), r.callingUid, true);}queue.lastProcessState = app.mState.getCurProcState();// 动态分发if (receiver instanceof BroadcastFilter) {notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);thread.scheduleRegisteredReceiver(((BroadcastFilter) receiver).receiverList.receiver,receiverIntent, r.resultCode, r.resultData, r.resultExtras,r.ordered, r.initialSticky, assumeDelivered, r.userId,app.mState.getReportedProcState(),r.shareIdentity ? r.callingUid : Process.INVALID_UID,r.shareIdentity ? r.callerPackage : null);// TODO: consider making registered receivers of unordered// broadcasts report results to detect ANRs// 如果无序广播,直接标记分发状态为DELIVEREDif (assumeDelivered) {finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_DELIVERED,"assuming delivered");return false;}} else {// 静态分发notifyScheduleReceiver(app, r, (ResolveInfo) receiver);thread.scheduleReceiver(receiverIntent, ((ResolveInfo) receiver).activityInfo,null, r.resultCode, r.resultData, r.resultExtras, r.ordered,assumeDelivered, r.userId,app.mState.getReportedProcState(),r.shareIdentity ? r.callingUid : Process.INVALID_UID,r.shareIdentity ? r.callerPackage : null);}// 有序或静态则block下个广播的分发return true;} catch (RemoteException e) {final String msg = "Failed to schedule " + r + " to " + receiver+ " via " + app + ": " + e;logw(msg);app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);// If we were trying to deliver a manifest broadcast, throw the error as we need// to try redelivering the broadcast to this receiver.if (receiver instanceof ResolveInfo) {mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);throw new BroadcastDeliveryFailedException(e);}finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,"remote app");return false;}} else {// 进程不存在finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,"missing IApplicationThread");return false;}
}
如果应假定此接收者已交付,则返回 true。
// BroadcastRecord.java
boolean isAssumedDelivered(int index) {return (receivers.get(index) instanceof BroadcastFilter) && !ordered&& (resultTo == null);
}
结束分发
有序广播或静态注册需要等待app执行完后binder回调finishReceiverLocked方法结束分发,运行在binder线程。
// BroadcastQueueModernImpl.java@Overridepublic boolean finishReceiverLocked(@NonNull ProcessRecord app, int resultCode,@Nullable String resultData, @Nullable Bundle resultExtras, boolean resultAbort,boolean waitForServices) {final BroadcastProcessQueue queue = getProcessQueue(app);if ((queue == null) || !queue.isActive()) {logw("Ignoring finishReceiverLocked; no active broadcast for " + queue);return false;}final BroadcastRecord r = queue.getActive();final int index = queue.getActiveIndex();// 有序广播相关属性赋值if (r.ordered) {r.resultCode = resultCode;r.resultData = resultData;r.resultExtras = resultExtras;if (!r.isNoAbort()) {r.resultAbort = resultAbort;}}// 为了确保以单调方式更新“超出”高水位线,我们在可能跳过任何剩余的中止接收器之前完成此接收器finishReceiverActiveLocked(queue,BroadcastRecord.DELIVERY_DELIVERED, "remote app");// 当调用者中止有序广播时,我们将所有剩余的接收者标记为已跳过if (r.resultAbort) {for (int i = index + 1; i < r.receivers.size(); i++) {setDeliveryState(null, null, r, i, r.receivers.get(i),BroadcastRecord.DELIVERY_SKIPPED, "resultAbort");}}// 如果当前分发的广播达到限制,则从可运行列表中移除if (shouldRetire(queue)) {demoteFromRunningLocked(queue);return true;}// 我们进展顺利; 进入该过程的下一个广播queue.makeActiveNextPending();try {// 如果执行广播流程已达到限制,则从可运行列表中移除当前进程队列if (scheduleReceiverWarmLocked(queue)) {demoteFromRunningLocked(queue);return true;}} catch (BroadcastDeliveryFailedException e) {reEnqueueActiveBroadcast(queue);demoteFromRunningLocked(queue);return true;}return false;}
终止队列中所有活动的广播。
// BroadcastQueueModernImpl.javaprivate void finishReceiverActiveLocked(@NonNull BroadcastProcessQueue queue,@DeliveryState int deliveryState, @NonNull String reason) {if (!queue.isActive()) {logw("Ignoring finishReceiverActiveLocked; no active broadcast for " + queue);return;}final int cookie = traceBegin("finishReceiver");final ProcessRecord app = queue.app;final BroadcastRecord r = queue.getActive();final int index = queue.getActiveIndex();final Object receiver = r.receivers.get(index);// 标记终端传递状态setDeliveryState(queue, app, r, index, receiver, deliveryState, reason);// 处理anr超时if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {r.anrCount++;if (app != null && !app.isDebugging()) {final String packageName = getReceiverPackageName(receiver);final String className = getReceiverClassName(receiver);mService.appNotResponding(queue.app,TimeoutRecord.forBroadcastReceiver(r.intent, packageName, className));}} else {// 移除超时消息mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_HARD, queue);}// 假设接收方刚刚完成,检查是否满足“waitingFor”条件。checkAndRemoveWaitingFor();traceEnd(cookie);}
设置给定广播的传递状态,然后应用与有序广播相关的任何额外簿记。
// BroadcastQueueModernImpl.javaprivate void setDeliveryState(@Nullable BroadcastProcessQueue queue,@Nullable ProcessRecord app, @NonNull BroadcastRecord r, int index,@NonNull Object receiver, @DeliveryState int newDeliveryState,@NonNull String reason) {final int cookie = traceBegin("setDeliveryState");// 记住旧状态并应用新状态final int oldDeliveryState = getDeliveryState(r, index);final boolean beyondCountChanged = r.setDeliveryState(index, newDeliveryState, reason);// 当我们改变交付状态作为从队列运行的一部分时,发出任何相关的跟踪结果if (queue != null) {if (newDeliveryState == BroadcastRecord.DELIVERY_SCHEDULED) {queue.traceActiveBegin();} else if ((oldDeliveryState == BroadcastRecord.DELIVERY_SCHEDULED)&& isDeliveryStateTerminal(newDeliveryState)) {queue.traceActiveEnd();}}// 如果我们进入最终状态,我们可能会有内部簿记来更新有序广播if (!isDeliveryStateTerminal(oldDeliveryState)&& isDeliveryStateTerminal(newDeliveryState)) {if (DEBUG_BROADCAST&& newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) {logw("Delivery state of " + r + " to " + receiver+ " via " + app + " changed from "+ deliveryStateToString(oldDeliveryState) + " to "+ deliveryStateToString(newDeliveryState) + " because " + reason);}notifyFinishReceiver(queue, app, r, index, receiver);}//当我们达到新的高水位线时,我们可能能够解锁其他接收者或最终结果if (beyondCountChanged) {if (r.beyondCount == r.receivers.size()) {// 已经派发完成,则回调最终receiverscheduleResultTo(r);}// 我们这里的终端状态可能足以让我们阻塞的另一个进程现在可以运行if (r.ordered || r.prioritized) {for (int i = 0; i < r.receivers.size(); i++) {if (!isDeliveryStateTerminal(getDeliveryState(r, i)) || (i == index)) {final Object otherReceiver = r.receivers.get(i);final BroadcastProcessQueue otherQueue = getProcessQueue(getReceiverProcessName(otherReceiver),getReceiverUid(otherReceiver));if (otherQueue != null) {otherQueue.invalidateRunnableAt();updateRunnableList(otherQueue);}}}enqueueUpdateRunningList();}}traceEnd(cookie);}
相关文章:

android U广播详解(二)
android U广播详解(一) 基础代码介绍 广播相关 // 用作单个进程批量分发receivers,已被丢弃 frameworks/base/services/core/java/com/android/server/am/BroadcastReceiverBatch.java // 主要逻辑所在类,包括入队、分发、结束…...

导航守卫的使用记录和beforeEach( )死循环的问题
前置导航守卫beforeEach的使用 import Vue from vue import VueRouter from vue-router // 进度条 import NProgress from nprogress import nprogress/nprogress.cssVue.use(VueRouter)// 路由表 const routes [{path: "/",redirect: "/home",},{path: …...

SpringMVC源码分析(三)HandlerExceptionResolver启动和异常处理源码分析
问题:异常处理器在SpringMVC中是如何进行初始化以及使用的? Spring MVC提供处理异常的方式主要分为两种: 1、实现HandlerExceptionResolver方式(HandlerExceptionResolver是一个接口,在SpringMVC有一些默认的实现也可以…...

系统架构与Tomcat的安装和配置
2023.10.16 今天是学习javaweb的第一天,主要学习了系统架构的相关知识和原理,下载了web服务器软件:Tomcat,并对其进行了配置。 系统架构 包括:C/S架构 和 B/S架构。 C/S架构: Client / Server࿰…...

【Shell脚本】根据起止日期获取Alert日志内容
【Shell脚本】根据起止日期获取Alert日志内容 根据输入的起止日期字符串,检索Oracle告警日志,打印中间的日志行内容。 #!/bin/bash # $1 START_TIME_STR, e.g. "Oct 17 07:" # $2 END_TIME_STR, e.g. "Oct 17 08:" source /home/o…...

Library projects cannot set applicationId. applicationId is set to
Library projects cannot set applicationId. applicationId is set to com.xxx.library_cache in default config. 删掉即可...

【兔子王赠书第2期】《案例学Python(基础篇)》
文章目录 前言推荐图书本书特色本书目录本书样章本书读者对象粉丝福利丨评论免费赠书尾声 前言 随着人工智能和大数据的蓬勃发展,Python将会得到越来越多开发者的喜爱和应用。身边有很多朋友都开始使用Python语言进行开发。正是因为Python是一门如此受欢迎的编程语…...

用户行为数据案例
一、环境要求 HadoopHiveSparkHBase 开发环境。 二、数据描述 本数据集包含了2017-09-11至2017-12-03之间有行为的约5458位随机用户的所有行为(行为包括点击、购买、加购、喜欢)。数据集的每一行表示一条用户行为,由用户ID、商品ID、商品类…...

selenium教程 —— css定位
说明:本篇博客基于selenium 4.1.0 selenium-css定位 element_css driver.find_element(By.CSS_SELECTOR, css表达式) 复制代码 css定位说明 selenium中的css定位,实际是通过css选择器来定位到具体元素,css选择器来自于css语法 css定位优点…...

Leetcode 1834. Single-Threaded CPU (堆好题)
Single-Threaded CPU Medium You are given n tasks labeled from 0 to n - 1 represented by a 2D integer array tasks, where tasks[i] [enqueueTimei, processingTimei] means that the ith task will be available to process at enque…...

21-数据结构-内部排序-交换排序
简介:主要根据两个数据进行比较从而交换彼此位置,以此类推,交换完全部。主要有冒泡和快速排序两种。 目录 一、冒泡排序 1.1简介: 1.2代码: 二、快速排序 1.1简介: 1.2代码: 一、冒泡排序…...

5-k8s-探针介绍
文章目录 一、探针介绍二、探针类型三、探针定义方式四、探针实例五、启动探针测试六、存活探针测试七、就绪探针测试 一、探针介绍 概念 在 Kubernetes 中 Pod 是最小的计算单元,而一个 Pod 又由多个容器组成,相当于每个容器就是一个应用,应…...

【网络安全 --- MySQL数据库】网络安全MySQL数据库应该掌握的知识,还不收藏开始学习。
四,MySQL 4.1 mysql安装 #centos7默认安装的是MariaDB-5.5.68或者65, #查看版本的指令:[rootweb01 bbs]# rpm -qa| grep mariadb #安装mariadb的最新版,只是更新了软件版本,不会删除之前原有的数据。 #修改yum源的配…...

【MyBatis系列】- 什么是MyBatis
【MyBatis系列】- 什么是MyBatis 文章目录 【MyBatis系列】- 什么是MyBatis一、学习MyBatis知识必备1.1 学习环境准备1.2 学习前掌握知识二、什么是MyBatis三、持久层是什么3.1 为什么需要持久化服务3.2 持久层四、Mybatis的作用五、MyBatis的优点六、参考文档一、学习MyBatis知…...

【Linux】Ubuntu美化bash【教程】
【Linux】Ubuntu美化bash【教程】 文章目录 【Linux】Ubuntu美化bash【教程】1. 查看当前环境中是否有bash2. 安装Synth-Shell3. 配置Synth-Shell4. 取消greeterReference 1. 查看当前环境中是否有bash 查看当前使用的bash echo $SHELL如下所示 sjhsjhR9000X:~$ echo $SHELL…...

微信小程序仿苹果负一屏由弱到强的高斯模糊
进入下面小程序可以体验效果,然后进入更多。查看模糊效果 一、创建小程序组件 二、代码 wxml: <view class"topBar-15"></view> <view class"topBar-14"></view> <view class"topBar-13"></view&…...

js中的new方法
new方法的作用:创建一个实例对象,并继承原对象的属性和方法; new对象内部操作: 1,创建一个新对象,将新对象的proto属性指向原对象的prototype属性; 2,构造函数执行环境中的this指向…...

机器学习-无监督算法之降维
降维:将训练数据中的样本从高维空间转换到低维空间,降维是对原始数据线性变换实现的。为什么要降维?高维计算难,泛化能力差,防止维数灾难优点:减少冗余特征,方便数据可视化,减少内存…...

ubuntu20.04下Kafka安装部署及基础使用
Ubuntu安装kafka基础使用 kafka 安装环境基础安装下载kafka解压文件修改配置文件启动kafka创建主题查看主题发送消息接收消息 工具测试kafka Assistant 工具连接测试基础连接连接成功查看topic查看消息查看分区查看消费组 Idea 工具测试基础信息配置信息当前消费组发送消息消费…...

汉得欧洲x甄知科技 | 携手共拓全球化布局,助力出海中企数智化发展
HAND Europe 荣幸获得华为云颁发的 GrowCloud 合作伙伴奖项,进一步巩固了其在企业数字化领域的重要地位。于 2023 年 10 月 5 日,HAND Europe 参加了华为云荷比卢峰会,并因其在全球拓展方面的杰出贡献而荣获 GrowCloud 合作伙伴奖项的认可。 …...

【Javascript保姆级教程】显示类型转换和隐式类型转换
文章目录 前言一、显式类型转换1.1 字符串转换1.2 数字转换1.3 布尔值转换 二、隐式类型转换2.1 数字与字符串相加2.2 布尔值与数字相乘 总结 前言 JavaScript是一种灵活的动态类型语言,这意味着变量的数据类型可以在运行时自动转换,或者通过显式类型转…...

C++算法前缀和的应用:分割数组的最大值的原理、源码及测试用例
分割数组的最大值 相关知识点 C算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例:付视频课程 二分 过些天整理基础知识 题目 给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。 设计一个算法…...

gitlab自编译 源码下载
网上都是怎么用 gitlab,但是实际开发中有需要针对 gitlab 进行二次编译自定义实现功能的想法。 搜索了网上的资料以及在官网的查找,查到了如下 gitlab 使用 ruby 开发。 gitlab 下载包 gitlab/gitlab-ce - Packages packages.gitlab.com gitlab/gitl…...

SBD(Schottky Barrier Diode)与JBS(Junction Barrier Schottky)
SBD和JBS二极管都是功率二极管,具有单向导电性,在电路中主要用于整流、箝位、续流等应用。两者的主要区别在于结构和性能。 结构 SBD是肖特基二极管的简称,其结构由一个金属和一个半导体形成的金属-半导体结构成。 JBS是结势垒肖特基二极…...

HANA:计算视图-图形化Aggregation组件-踩坑小记(注意事项)
今天遇到在做HANA视图开发的时候,遇到一个事,一直以为是个BUG,可把我气坏了,具体逻辑是这样的,是勇图形化处理的,ACDOCA innerjoin 一个时间维度表,就这么简单,完全按照ACDOCA的主键…...

【milkv】更新rndis驱动
问题 由于windows升级到了11,导致rndis驱动无法识别到。 解决 打开设备管理器,查看网络适配器,没有更新会显示黄色的图标。 右击选择更新驱动...

基于混沌博弈优化的BP神经网络(分类应用) - 附代码
基于混沌博弈优化的BP神经网络(分类应用) - 附代码 文章目录 基于混沌博弈优化的BP神经网络(分类应用) - 附代码1.鸢尾花iris数据介绍2.数据集整理3.混沌博弈优化BP神经网络3.1 BP神经网络参数设置3.2 混沌博弈算法应用 4.测试结果…...

基于人工水母优化的BP神经网络(分类应用) - 附代码
基于人工水母优化的BP神经网络(分类应用) - 附代码 文章目录 基于人工水母优化的BP神经网络(分类应用) - 附代码1.鸢尾花iris数据介绍2.数据集整理3.人工水母优化BP神经网络3.1 BP神经网络参数设置3.2 人工水母算法应用 4.测试结果…...

【C++】哈希学习
哈希学习 unordered系列关联式容器哈希结构除留余数法哈希冲突闭散列线性探测二次探测 负载因子开散列开散列增容 闭散列 VS 开散列字符串哈希算法 线性探测 & 二次探测实现拉链法实现 unordered系列关联式容器 unordered系列关联式容器是从C11开始,STL提供的。…...

Nginx的安装——window环境
1、下载Nginx 在官网下载稳定版本: http://nginx.org/en/download.html 以nginx/Windows-1.24.0为例,直接下载 nginx-1.24.0.zip。 下载后解压,解压后如下: 2、启动nginx 在window环境下启动nginx的方法有以下两种: …...