There are several types of broadcast dispatches, but we’ll focus on unordered broadcast dispatches.
To send an unordered broadcast, we call the sendBroadcast method, which eventually calls the sendBroadcast method in ContextImpl.
ContextImpl to AMS
Sequence diagram:
Let’s look at the ContextImpl method first
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null.null.null, AppOpsManager.OP_NONE, null.false.false,
getUserId());
} catch (RemoteException e) {
throwe.rethrowFromSystemServer(); }}Copy the code
This is where AMS’s broadcastIntent method is eventually called.
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this){ intent = verifyBroadcastLocked(intent); final ProcessRecord callerApp = getRecordForAppLocked(caller); final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); int res = broadcastIntentLocked(callerApp, callerApp ! =null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, appOp, bOptions, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
returnres; }}Copy the code
In AMS’s broadcastIntent method, we first call verifyBroadcastLocked to check whether the broadcast is valid.
So let’s see how it’s detected, right
final Intent verifyBroadcastLocked(Intent intent) {
// Determine if the intent has a descriptor
if(intent ! =null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
// Get the flag set by the intent
int flags = intent.getFlags();
if(! mProcessesReady) {// If the system is booting, this method is entered
If the intent is set to FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, no action is taken
if((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) ! =0) {
// This will be turned into a FLAG_RECEIVER_REGISTERED_ONLY later on if needed.
} else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
// If the intent is not set to FLAG_RECEIVER_REGISTERED_ONLY, an error is reported.
Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
+ " before boot completion");
throw new IllegalStateException("Cannot broadcast before boot completed"); }}if((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) ! =0) {
throw new IllegalArgumentException(
"Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
}
if((flags & Intent.FLAG_RECEIVER_FROM_SHELL) ! =0) {
switch (Binder.getCallingUid()) {
case ROOT_UID:
case SHELL_UID:
break;
default:
Slog.w(TAG, "Removing FLAG_RECEIVER_FROM_SHELL because caller is UID "
+ Binder.getCallingUid());
intent.removeFlags(Intent.FLAG_RECEIVER_FROM_SHELL);
break; }}return intent;
}
Copy the code
After checking, we call the broadcastIntentLocked method of the broadcastIntent
final int broadcastIntentLocked(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 userId) {
...
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if(! ordered && NR >0) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
final BroadcastQueue queue = broadcastQueueForIntent(intent);
// Create a BroadcastRecord
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
resultCode, resultData, resultExtras, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast "+ r); final boolean replaced = replacePending && (queue.replaceParallelBroadcastLocked(r) ! =null);
// Note: We assume resultTo is null for non-ordered broadcasts.
if(! replaced) {// Add the BroadcastRecord to the queue
queue.enqueueParallelBroadcastLocked(r);
// Call scheduleBroadcastsLocked of the BroadcastQueue
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0; }...return ActivityManager.BROADCAST_SUCCESS;
}
Copy the code
Next we’ll look at the broadcast that needs to be sent from AMS to the BroadcastReceiver process
AMS to BroadcastReceiver process
Sequence diagram:
Let’s continue with scheduleBroadcastsLocked
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
Copy the code
An mHandler of type BroadcastHandler sends a message identified as BROADCAST_INTENT_MSG
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
processNextBroadcast(true);
} break;
Copy the code
The processNextBroadcast method is also called in the Handler. The processNextBroadcast method handles both out-of-order and ordered broadcasts.
final void processNextBroadcast(boolean fromMsg) { ... if (fromMsg) { mBroadcastsScheduled = false; } / / traverse stored disorderly radio while (mParallelBroadcasts. The size () > 0) {/ / get disorderly radio r = mParallelBroadcasts. Remove (0); r.dispatchTime = SystemClock.uptimeMillis(); r.dispatchClockTime = System.currentTimeMillis(); . 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); deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i); } addBroadcastToHistoryLocked(r); if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast [" + mQueueName + "] " + r); }}Copy the code
Radio here first to get to the chaos, and then call deliverToRegisteredReceiverLocked method, and access to radio instance into the inside. Take a look at deliverToRegisteredReceiverLocked method
private void deliverToRegisteredReceiverLocked(BroadcastRecord r, BroadcastFilter filter, boolean ordered, int index) { ... try { if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST, "Delivering to " + filter + " : " + r); if (filter.receiverList.app ! = null && filter.receiverList.app.inFullBackup) { // Skip delivery if full backup in progress // If it's an ordered broadcast, we need to continue to the next receiver. if (ordered) { skipReceiverLocked(r); } } else { performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky, r.userId); } if (ordered) { r.state = BroadcastRecord.CALL_DONE_RECEIVE; } } catch (RemoteException e) { Slog.w(TAG, "Failure sending broadcast " + r.intent, e); if (ordered) { r.receiver = null; r.curFilter = null; filter.receiverList.curBroadcast = null; if (filter.receiverList.app ! = null) { filter.receiverList.app.curReceivers.remove(r); }}}}Copy the code
Most of the code, which checks for things related to broadcast permissions, is omitted here, and if all checks pass, the performReceiveLocked method is called.
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException { // Send the intent to the receiver asynchronously using one-way binder calls. if (app ! = null) { if (app.thread ! = null) { // If we have an app thread, do the call through that so it is // correctly ordered with other one-way calls. try { app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras, ordered, sticky, sendingUser, app.repProcState); // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting // DeadObjectException when the process isn't actually dead. //} catch (DeadObjectException ex) { // Failed to call into the process. It's dying so just let it die and move on. // throw ex; } catch (RemoteException ex) { // Failed to call into the process. It's either dying or wedged. Kill it gently. synchronized (mService) { Slog.w(TAG, "Can't deliver broadcast to " + app.processName + " (pid " + app.pid + "). Crashing it."); app.scheduleCrash("can't deliver broadcast"); } throw ex; } } else { // Application has died. Receiver doesn't exist. throw new RemoteException("app.thread must not be null"); } } else { receiver.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser); }}Copy the code
To judge whether the process of the broadcast receiver exists, if there is a will call ApplicationThread scheduleRegisteredReceiver method. This brings us back to the client side
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState) throws RemoteException {
updateProcessState(processState, false);
receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
sticky, sendingUser);
}
Copy the code
The performReceive method of the IIntentReceiver object is called. IIntentReceiver is used to broadcast cross-process communication. Implemented as LoadedApk. ReceiverDispatcher. InnerReceiver.
@Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { final LoadedApk.ReceiverDispatcher rd; if (intent == null) { Log.wtf(TAG, "Null intent received"); rd = null; } else { rd = mDispatcher.get(); } if (ActivityThread.DEBUG_BROADCAST) { int seq = intent.getIntExtra("seq", -1); Slog.i(ActivityThread.TAG, "Receiving broadcast " + intent.getAction() + " seq=" + seq + " to " + (rd ! = null ? rd.mReceiver : null)); } if (rd ! = null) { rd.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser); } else { ... }}Copy the code
The performReceive method of ReceiverDispatcher is called
public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { final Args args = new Args(intent, resultCode, data, extras, ordered, sticky, sendingUser); if (intent == null) { Log.wtf(TAG, "Null intent received"); } else { if (ActivityThread.DEBUG_BROADCAST) { int seq = intent.getIntExtra("seq", -1); Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq + " to " + mReceiver); } } if (intent == null || ! mActivityThread.post(args.getRunnable())) { if (mRegistered && ordered) { IActivityManager mgr = ActivityManager.getService(); if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, "Finishing sync broadcast to " + mReceiver); args.sendFinished(mgr); }}}}Copy the code
Encapsulate the broadcast intent parameters as Args, and then call the mActivityThread post method to pass in the Args. MActivityThread is a Handler that points to H. Since args.geTrunnable () is passed in, what does getRunnable do
final class Args extends BroadcastReceiver.PendingResult { ... public final Runnable getRunnable() { return () -> { ... try { ClassLoader cl = mReceiver.getClass().getClassLoader(); intent.setExtrasClassLoader(cl); intent.prepareToEnterProcess(); setExtrasClassLoader(cl); receiver.setPendingResult(this); receiver.onReceive(mContext, intent); } catch (Exception e) { ... }... }; }}Copy the code
The onReceive method of receiver is called so that registered broadcasts can be notified.