// Collect stickies of users int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) }; while (actions.hasNext()) { Stringaction= actions.next(); for (int id : userIds) { ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id); if (stickies != null) { ArrayList<Intent> intents = stickies.get(action); if (intents != null) { if (stickyIntents == null) { stickyIntents = newArrayList<Intent>(); } stickyIntents.addAll(intents); } } } } } //离开临界区
ArrayList<Intent> allSticky = null; if (stickyIntents != null) { finalContentResolverresolver= mContext.getContentResolver(); // Look for any matching sticky broadcasts... for (inti=0, N = stickyIntents.size(); i < N; i++) { Intentintent= stickyIntents.get(i); ...... if (filter.match(resolver, intent, true, TAG) >= 0) { if (allSticky == null) { allSticky = newArrayList<Intent>(); } allSticky.add(intent); } } }
// The first sticky in the list is returned directly back to the client. Intentsticky= allSticky != null ? allSticky.get(0) : null; ... if (receiver == null) { return sticky; }
// Enqueue broadcasts for all existing stickies that match // this filter. if (allSticky != null) { ArrayListreceivers=newArrayList(); receivers.add(bf);
finalintstickyCount= allSticky.size(); for (inti=0; i < stickyCount; i++) { Intentintent= allSticky.get(i); BroadcastQueuequeue= broadcastQueueForIntent(intent); BroadcastRecordr=newBroadcastRecord(queue, intent, null, null, -1, -1, false, null, null, OP_NONE, null, receivers, null, 0, null, null, false, true, true, -1, false, false/* only PRE_BOOT_COMPLETED should be exempt, no stickies */); queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } }
finalintbroadcastIntentLocked(ProcessRecord callerApp, String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, int realCallingPid, int userId, boolean allowBackgroundActivityStarts) { intent = newIntent(intent); ...... // By default broadcasts do not go to stopped apps. intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); // If we have not finished booting, don't allow this to launch new processes. if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); } userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_NON_FULL, "broadcast", callerPackage);
// Make sure that the user who is receiving this broadcast or its parent is running. // If not, we will just skip it. Make an exception for shutdown broadcasts, upgrade steps. if (userId != UserHandle.USER_ALL && !mUserController.isUserOrItsParentRunning(userId)) { if ((callingUid != SYSTEM_UID || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { Slog.w(TAG, "Skipping broadcast of " + intent + ": user " + userId + " and its parent (if any) are stopped"); return ActivityManager.BROADCAST_FAILED_USER_STOPPED; } }
// First line security check before anything else: stop non-system apps from // sending protected broadcasts. if (!isCallerSystem) { if (isProtectedBroadcast) { ...... thrownewSecurityException(msg);
if (sticky) { if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,...)...) {...} if (requiredPermissions != null && requiredPermissions.length > 0) {...} if (intent.getComponent() != null) {...} // We use userId directly here, since the "all" target is maintained // as a separate set of sticky broadcasts. if (userId != UserHandle.USER_ALL) { // But first, if this is not a broadcast to all users, then // make sure it doesn't conflict with an existing broadcast to // all users. ...... } ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId); if (stickies == null) { stickies = newArrayMap<>(); mStickyBroadcasts.put(userId, stickies); } ArrayList<Intent> list = stickies.get(intent.getAction()); if (list == null) { list = newArrayList<>(); stickies.put(intent.getAction(), list); } finalintstickiesCount= list.size(); int i; for (i = 0; i < stickiesCount; i++) { if (intent.filterEquals(list.get(i))) { // This sticky already exists, replace it. list.set(i, newIntent(intent)); break; } } if (i >= stickiesCount) { list.add(newIntent(intent)); } }
这一段处理粘性广播(Sticky Broadcast)。
所有粘性广播列表保存在 AMS 的 mStickyBroadcasts 成员中。最外层的 SparseArray 以 user ID 为 key,中层的 ArrayMap 以 Intent 的 action 为 key,相同 user ID 、相同 action 的 Intent 保存在 ArrayList 中。
1
final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts = ...;
int[] users; if (userId == UserHandle.USER_ALL) { // Caller wants broadcast to go to all started users. users = mUserController.getStartedUserArray(); } else { // Caller wants broadcast to go to one specific user. users = newint[] {userId}; }
// Merge into one list. intir=0; if (receivers != null) { // 不允许应用接收自身的 PACKAGE_ADDED 广播,以免其利用该广播自启动。 ...... intNT= receivers != null ? receivers.size() : 0; intit=0; ResolveInfocurt=null; BroadcastFiltercurr=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 = newArrayList(); } receivers.add(registeredReceivers.get(ir)); ir++; }
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
finalBroadcastRecordoldRecord= replacePending ? queue.replaceOrderedBroadcastLocked(r) : null; if (oldRecord != null) { // Replaced, fire the result-to receiver. if (oldRecord.resultTo != null) { finalBroadcastQueueoldQueue= broadcastQueueForIntent(oldRecord.intent); try { oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo, oldRecord.intent, Activity.RESULT_CANCELED, null, null, false, false, oldRecord.userId); } catch (RemoteException e) {...} } } else { queue.enqueueOrderedBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } } else { // There was nobody interested in the broadcast, but we still want to record // that it happened. if (intent.getComponent() == null && intent.getPackage() == null && (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { // This was an implicit broadcast... let's record it for posterity. addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0); } }
do { // while (r == null); finallongnow= SystemClock.uptimeMillis(); r = mDispatcher.getNextBroadcastLocked(now);
if (r == null) { .....// No more broadcasts are deliverable right now, so all done! return; } booleanforceReceive=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. intnumReceivers= (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))) { ... broadcastTimeoutLocked(false); // forcibly finish this broadcast forceReceive = true; r.state = BroadcastRecord.IDLE; } }
if (r.state != BroadcastRecord.IDLE) { ... return; }
进入循环,这个循环用来搜索下一个需要发送的广播并将其取到变量 r,只有当 r != null 时才会退出循环。
// In do {...} while (r == null) // Is the current broadcast is done for any reason? if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) { // Send the final result if requested if (r.resultTo != null) { booleansendResult=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) { intnewCount= mSplitRefcounts.get(r.splitToken) - 1; if (newCount == 0) { ...// done! clear out this record's bookkeeping and deliver mSplitRefcounts.delete(r.splitToken); } else { // still have some split broadcast records in flight; update refcount // and hold off on the callback ... sendResult = false; mSplitRefcounts.put(r.splitToken, newCount); } } if (sendResult) { try { ... performReceiveLocked(r.callerApp, r.resultTo, newIntent(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) {...} } } ...... // ... and on to the next... addBroadcastToHistoryLocked(r); 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; }
// do { ... if (!r.deferred) { finalintreceiverUid= r.getReceiverUid(r.receivers.get(r.nextReceiver)); if (mDispatcher.isDeferringLocked(receiverUid)) { ... // 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) { ... 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); ... // Track completion refcount as well if relevant if (r.resultTo != null) { inttoken= 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); ... } else { // new split from an already-refcounted situation; increment count finalintcurCount= mSplitRefcounts.get(token); ... mSplitRefcounts.put(token, curCount + 1); ... } } } mDispatcher.addDeferredBroadcast(receiverUid, defer); r = null; looped = true; continue; } } } while (r == null);
// Get the next receiver... intrecIdx= r.nextReceiver++;
// Keep track of when this receiver started, and make sure there // is a timeout message pending to kill it if need be. ...... finalBroadcastOptionsbrOptions= r.options; finalObjectnextReceiver= r.receivers.get(recIdx);
if (nextReceiver instanceof BroadcastFilter) { // Simple case: this is a registered receiver who gets // a direct call. BroadcastFilterfilter= (BroadcastFilter)nextReceiver; ... deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx); if (r.receiver == null || !r.ordered) { // The receiver has already finished, so schedule to // process the next one. ... r.state = BroadcastRecord.IDLE; scheduleBroadcastsLocked(); } else {...} return; }
// Hard case: need to instantiate the receiver, possibly // starting its application process to host it. ResolveInfoinfo= (ResolveInfo)nextReceiver; ComponentNamecomponent=newComponentName( info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
// 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) {...}
// 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) { ... } catch (RuntimeException e) { // If some unexpected exception happened, just skip // this broadcast. ...... // finishReceiverLocked; ScheduleBroadcastLocked; state = 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 ((r.curApp=mService.startProcessLocked(...)) == null) { // 应用启动失败,跳过广播 ...... // finishReceiverLocked; ScheduleBroadcastLocked; state = IDLE; return; }
voidperformReceiveLocked(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.getReportedProcState()); } catch (RemoteException ex) {...} //throw ex } else {...} //throw RemoteException } else { receiver.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser); } }
// We will process the next receiver right now if this is finishing // an app receiver (which is always asynchronous) or after we have // come back from calling a receiver. return state == BroadcastRecord.APP_RECEIVE || state == BroadcastRecord.CALL_DONE_RECEIVE; }