The broadcast registration process was analyzed in the previous section, so what steps did sendBroadcast go through?

The onStartCommand of DemoService calls sendBroadcast with a String before it is sent.

package com.demo.framework

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log

class DemoService : Service() {

    companion object {
        const val TAG = "DemoService"
    }

    override fun onBind(intent: Intent): IBinder? {
        Log.d(TAG, "onBind")
        return null
    }

    override fun onCreate(a) {
        Log.d(TAG, "onCreate")
        super.onCreate()
    }

    override fun onStartCommand(intent: Intent? , flags:Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand flags=$flags,startId=$startId")
        val intent = Intent(MainActivity.ACTION)
        intent.putExtra(MainActivity.VALUE_KEY, "DemoService Hello!")
        sendBroadcast(intent)
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy(a) {
        Log.d(TAG, "onDestroy")
        super.onDestroy()
    }
}
Copy the code

The sendBroadcast method comes from the ContextWrapper class and ends up calling the sendBroadcast method in the ContextImpl class.

frameworks/base/core/java/android/content/ContextWrapper.java

public class ContextWrapper extends Context {...@Override
    public void sendBroadcast(Intent intent) { mBase.sendBroadcast(intent); }... }Copy the code

The ContextImpl class takes the ActivityManagerProxy object from the sendBroadcast method and calls its broadcastIntent method. MMainThread. GetApplicationThread () returns ApplicationThread object.

frameworks/base/core/java/android/app/ContextImpl.java

class ContextImpl extends Context {...@Override
    public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess();
            ActivityManagerNative.getDefault().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null.null.null, AppOpsManager.OP_NONE, null.false.false,
                    getUserId());
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e); }}... }Copy the code

The broadcastIntent method of ActivityManagerProxy ends up calling its own mRemote, a Binder proxy object, to send ActivityManagerService a message of type BROADCAST_INTENT_TRANSACTION Interprocess communication requests.

frameworks/base/core/java/android/app/ActivityManagerNative.java

class ActivityManagerProxy implements IActivityManager
{...public int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle map,
            String[] requiredPermissions, int appOp, Bundle options, boolean serialized,
            boolean sticky, int userId) throws RemoteException
    { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller ! =null ? caller.asBinder() : null);
        intent.writeToParcel(data, 0); data.writeString(resolvedType); data.writeStrongBinder(resultTo ! =null ? resultTo.asBinder() : null);
        data.writeInt(resultCode);
        data.writeString(resultData);
        data.writeBundle(map);
        data.writeStringArray(requiredPermissions);
        data.writeInt(appOp);
        data.writeBundle(options);
        data.writeInt(serialized ? 1 : 0);
        data.writeInt(sticky ? 1 : 0);
        data.writeInt(userId);
        mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
        reply.readException();
        int res = reply.readInt();
        reply.recycle();
        data.recycle();
        returnres; }... }Copy the code

This function then calls the broadcastIntentLocked method.

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor.BatteryStatsImpl.BatteryCallback {...public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle resultExtras,
            String[] requiredPermissions, int appOp, Bundle options,
            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();
            intres = broadcastIntentLocked(callerApp, callerApp ! =null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, appOp, null, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            returnres; }}... }Copy the code

The above functions prepare the necessary parameters, and the next step is to call broadcastIntentLocked.

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor.BatteryStatsImpl.BatteryCallback {...private final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle options,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
        intent = new Intent(intent);

        // By default, broadcasts are not forwarded to stopped applications
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

        // New processes cannot be allowed to start if they have not finished
        if(! mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) ==0) {
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        }

        userId = handleIncomingUser(callingPid, callingUid, userId,
                true, ALLOW_NON_FULL, "broadcast", callerPackage);

        // Make sure the user receiving this broadcast is running. If not, we'll skip it.
        if(userId ! = UserHandle.USER_ALL && ! isUserRunningLocked(userId,false)) {
            if((callingUid ! = Process.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 + " is stopped");
                return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
            }
        }

        BroadcastOptions brOptions = null;
        if(options ! =null) {
            brOptions = new BroadcastOptions(options);
            if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
                // Check whether the caller is allowed to do so. Note that we are checking the actual real caller, because who actually provides the parameters.
                if (checkComponentPermission(
                        android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
                        Binder.getCallingPid(), Binder.getCallingUid(), -1.true)
                        != PackageManager.PERMISSION_GRANTED) {
                    String msg = "Permission Denial: " + intent.getAction()
                            + " broadcast from " + callerPackage + " (pid=" + callingPid
                            + ", uid=" + callingUid + ")"
                            + " requires "
                            + android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
                    Slog.w(TAG, msg);
                    throw newSecurityException(msg); }}}/* * Prevents non-system code (defined here as a non-persistent process) from sending protected broadcasts. * /
        int callingAppId = UserHandle.getAppId(callingUid);
        if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
            || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
            || callingAppId == Process.NFC_UID || callingUid == 0) {
            // Always okay.
        } else if (callerApp == null| |! callerApp.persistent) {try {
                if (AppGlobals.getPackageManager().isProtectedBroadcast(
                        intent.getAction())) {
                    String msg = "Permission Denial: not allowed to send broadcast "
                            + intent.getAction() + " from pid="
                            + callingPid + ", uid=" + callingUid;
                    Slog.w(TAG, msg);
                    throw new SecurityException(msg);
                } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(intent.getAction())) {
                    // Special case of compatibility: we don't want the application to send this,
                    // But historically it has not been protected, so rather than make it protected, restrict it from the caller.
                    if (callerApp == null) {
                        String msg = "Permission Denial: not allowed to send broadcast "
                                + intent.getAction() + " from unknown caller.";
                        Slog.w(TAG, msg);
                        throw new SecurityException(msg);
                    } else if(intent.getComponent() ! =null) {
                        // They are good enough to send to explicit components... Verify that it has been sent to the calling application.
                        if(! intent.getComponent().getPackageName().equals( callerApp.info.packageName)) { String msg ="Permission Denial: not allowed to send broadcast "
                                    + intent.getAction() + " to "
                                    + intent.getComponent().getPackageName() + " from "
                                    + callerApp.info.packageName;
                            Slog.w(TAG, msg);
                            throw newSecurityException(msg); }}else {
                        // Restrict broadcasting to your own package.intent.setPackage(callerApp.info.packageName); }}}catch (RemoteException e) {
                Slog.w(TAG, "Remote exception", e);
                returnActivityManager.BROADCAST_SUCCESS; }}final String action = intent.getAction();
        if(action ! =null) {
            switch (action) {
                case Intent.ACTION_UID_REMOVED:
                case Intent.ACTION_PACKAGE_REMOVED:
                case Intent.ACTION_PACKAGE_CHANGED:
                case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
                case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
                    // Handle special intent: If this broadcast is a message from the package manager about deleting a package,
                    // We need to remove all activities from the history stack.
                    
                    // If the permission is not met, a SecurityException is thrown
                    if (checkComponentPermission(
                            android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
                            callingPid, callingUid, -1.true)
                            != PackageManager.PERMISSION_GRANTED) {
                        String msg = "Permission Denial: " + intent.getAction()
                                + " broadcast from " + callerPackage + " (pid=" + callingPid
                                + ", uid=" + callingUid + ")"
                                + " requires "
                                + android.Manifest.permission.BROADCAST_PACKAGE_REMOVED;
                        Slog.w(TAG, msg);
                        throw new SecurityException(msg);
                    }
                    switch (action) {
                        case Intent.ACTION_UID_REMOVED:
                            final Bundle intentExtras = intent.getExtras();
                            final intuid = intentExtras ! =null
                                    ? intentExtras.getInt(Intent.EXTRA_UID) : -1;
                            if (uid >= 0) {
                                mBatteryStatsService.removeUid(uid);
                                mAppOpsService.uidRemoved(uid);
                            }
                            break;
                        case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
                            // If the resource is not available, force all these packages to stop and flush the property cache.
                            String list[] =
                                    intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                            if(list ! =null && list.length > 0) {
                                for (int i = 0; i < list.length; i++) {
                                    forceStopPackageLocked(list[i], -1.false.true.true.false.false, userId, "storage unmount");
                                }
                                mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
                                sendPackageBroadcastLocked(
                                        IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list,
                                        userId);
                            }
                            break;
                        case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
                            mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
                            break;
                        case Intent.ACTION_PACKAGE_REMOVED:
                        case Intent.ACTION_PACKAGE_CHANGED:
                            Uri data = intent.getData();
                            String ssp;
                            if(data ! =null&& (ssp=data.getSchemeSpecificPart()) ! =null) {
                                boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);
                                booleanfullUninstall = removed && ! intent.getBooleanExtra(Intent.EXTRA_REPLACING,false);
                                final booleankillProcess = ! intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP,false);
                                if (killProcess) {
                                    forceStopPackageLocked(ssp, UserHandle.getAppId(
                                            intent.getIntExtra(Intent.EXTRA_UID, -1)),
                                            false.true.true.false, fullUninstall, userId,
                                            removed ? "pkg removed" : "pkg changed");
                                }
                                if (removed) {
                                    sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
                                            new String[] {ssp}, userId);
                                    if (fullUninstall) {
                                        mAppOpsService.packageRemoved(
                                                intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);

                                        // Delete all permissions granted to this package
                                        removeUriPermissionsForPackageLocked(ssp, userId, true); removeTasksByPackageNameLocked(ssp, userId); mBatteryStatsService.notePackageUninstalled(ssp); }}else{ cleanupDisabledPackageComponentsLocked(ssp, userId, killProcess, intent.getStringArrayExtra( Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST)); }}break;
                    }
                    break;
                case Intent.ACTION_PACKAGE_ADDED:
                    // Special case for adding packages: Compatibility mode is turned on by default.
                    Uri data = intent.getData();
                    String ssp;
                    if(data ! =null&& (ssp = data.getSchemeSpecificPart()) ! =null) {
                        final boolean replacing =
                                intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
                        mCompatModePackages.handlePackageAddedLocked(ssp, replacing);

                        try {
                            ApplicationInfo ai = AppGlobals.getPackageManager().
                                    getApplicationInfo(ssp, 0.0); mBatteryStatsService.notePackageInstalled(ssp, ai ! =null ? ai.versionCode : 0);
                        } catch (RemoteException e) {
                        }
                    }
                    break;
                case Intent.ACTION_TIMEZONE_CHANGED:
                    // If this is a time zone change, queue a message to reset the time zones of all currently running processes. The message will be queued before the broadcast takes place.
                    mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
                    break;
                case Intent.ACTION_TIME_CHANGED:
                    // If the user sets the time, let all running processes know.
                    final int is24Hour =
                            intent.getBooleanExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false)?1
                                    : 0;
                    mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));
                    BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
                    synchronized (stats) {
                        stats.noteCurrentTimeChangedLocked();
                    }
                    break;
                case Intent.ACTION_CLEAR_DNS_CACHE:
                    mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
                    break;
                case Proxy.PROXY_CHANGE_ACTION:
                    ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);
                    mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
                    break; }}// Add to stickiness list if desired.
        if (sticky) {
            // Check the sticky broadcast permissions
            if(checkPermission(android.Manifest.permission.BROADCAST_STICKY, callingPid, callingUid) ! = PackageManager.PERMISSION_GRANTED) { String msg ="Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="
                        + callingPid + ", uid=" + callingUid
                        + " requires " + android.Manifest.permission.BROADCAST_STICKY;
                Slog.w(TAG, msg);
                throw new SecurityException(msg);
            }
            if(requiredPermissions ! =null && requiredPermissions.length > 0) {
                Slog.w(TAG, "Can't broadcast sticky intent " + intent
                        + " and enforce permissions " + Arrays.toString(requiredPermissions));
                return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
            }
            if(intent.getComponent() ! =null) {
                throw new SecurityException(
                        "Sticky broadcasts can't target a specific component");
            }
            // We use userId directly here, because 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 for all users, make sure it does not conflict with an existing broadcast for all users
                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
                        UserHandle.USER_ALL);
                if(stickies ! =null) {
                    ArrayList<Intent> list = stickies.get(intent.getAction());
                    if(list ! =null) {
                        int N = list.size();
                        int i;
                        for (i=0; i<N; i++) {
                            if (intent.filterEquals(list.get(i))) {
                                throw new IllegalArgumentException(
                                        "Sticky broadcast " + intent + " for user "
                                        + userId + " conflicts with existing global broadcast");
                            }
                        }
                    }
                }
            }
            ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
            if (stickies == null) {
                stickies = new ArrayMap<>();
                mStickyBroadcasts.put(userId, stickies);
            }
            ArrayList<Intent> list = stickies.get(intent.getAction());
            if (list == null) {
                list = new ArrayList<>();
                stickies.put(intent.getAction(), list);
            }
            final int stickiesCount = list.size();
            int i;
            for (i = 0; i < stickiesCount; i++) {
                if (intent.filterEquals(list.get(i))) {
                    // This stickiness already exists, replace it.
                    list.set(i, new Intent(intent));
                    break; }}if (i >= stickiesCount) {
                list.add(newIntent(intent)); }}int[] users;
        if (userId == UserHandle.USER_ALL) {
            // The caller wants to broadcast to all initiated users.
            users = mStartedUserArray;
        } else {
            // The caller wants to broadcast to a specific user.
            users = new int[] {userId};
        }

        // Find out who will receive this broadcast.
        List receivers = null;
        List<BroadcastFilter> registeredReceivers = null;
        // Need to resolve intent interested receiver...
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }
        if (intent.getComponent() == null) {
            if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
                // Query one target user at a time, excluding users restricted by shell
                UserManagerService ums = getUserManagerLocked();
                for (int i = 0; i < users.length; i++) {
                    if (ums.hasUserRestriction(
                            UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                        continue;
                    }
                    List<BroadcastFilter> registeredReceiversForUser =
                            mReceiverResolver.queryIntent(intent,
                                    resolvedType, false, users[i]);
                    if (registeredReceivers == null) {
                        registeredReceivers = registeredReceiversForUser;
                    } else if(registeredReceiversForUser ! =null) { registeredReceivers.addAll(registeredReceiversForUser); }}}else {
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false, userId); }}// This flag replaces the previous Intent.
        // After sending a series of intEnts,
        // Some intents can be superseded before you have a chance to handle them.
        final booleanreplacePending = (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) ! =0;

        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueing broadcast: " + intent.getAction()
                + " replacePending=" + replacePending);
        // Handle dynamic broadcast receivers
        intNR = registeredReceivers ! =null ? registeredReceivers.size() : 0;
        if(! ordered && NR >0) {
            // If we do not serialize the broadcast, we send it separately to the registered receiver so that we do not wait for the component to be started.
            We keep two broadcast queues and their associated bookkeeping, one for foreground priority and one for normal (background priority) broadcasts.
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            // Build a BroadcastRecord object, which contains much of the BroadcastRecord information
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
                    appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
                    resultExtras, ordered, sticky, false, userId);
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
            / / replaceParallelBroadcastLocked function checks whether the queue contains have the same intention BroadcastRecord, replaced here to false
            final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
            if(! replaced) { queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } registeredReceivers =null;
            NR = 0;
        }

        // Merge into a list.
        int ir = 0;
        if(receivers ! =null) {
            // Special case of PACKAGE_ADDED: Added packages are not allowed to see this broadcast.
            // This prevents them from using this as a backdoor to run immediately after installation.
            // Maybe in the future we want to provide a special setup broadcast or something like that for the application, but we want to make this decision carefully.
            String skipPackages[] = null;
            if(Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction()) || Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction()) ||  Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) { Uri data = intent.getData();if(data ! =null) {
                    String pkgName = data.getSchemeSpecificPart();
                    if(pkgName ! =null) {
                        skipPackages = newString[] { pkgName }; }}}else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
                skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
            }
            if(skipPackages ! =null && (skipPackages.length > 0)) {
                for (String skipPackage : skipPackages) {
                    if(skipPackage ! =null) {
                        int NT = receivers.size();
                        for (int it=0; it<NT; it++) {
                            ResolveInfo curt = (ResolveInfo)receivers.get(it);
                            if (curt.activityInfo.packageName.equals(skipPackage)) {
                                receivers.remove(it);
                                it--;
                                NT--;
                            }
                        }
                    }
                }
            }

            intNT = receivers ! =null ? receivers.size() : 0;
            int it = 0;
            ResolveInfo curt = null;
            BroadcastFilter curr = null;
            while (it < NT && ir < NR) {
                if (curt == null) {
                    curt = (ResolveInfo)receivers.get(it);
                }
                if (curr == null) {
                    curr = registeredReceivers.get(ir);
                }
                if (curr.getPriority() >= curt.priority) {
                    // Insert this broadcast record into the final list.
                    receivers.add(it, curr);
                    ir++;
                    curr = null;
                    it++;
                    NT++;
                } else {
                    // Skip to the next ResolveInfo in the final list.
                    it++;
                    curt = null; }}}while (ir < NR) {
            if (receivers == null) {
                receivers = new ArrayList();
            }
            receivers.add(registeredReceivers.get(ir));
            ir++;
        }
        // Handle static broadcast receivers
        if((receivers ! =null && receivers.size() > 0) || resultTo ! =null) {
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType,
                    requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                    resultData, resultExtras, ordered, sticky, false, userId);

            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
                    + ": prev had " + queue.mOrderedBroadcasts.size());
            if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
                    "Enqueueing broadcast " + r.intent.getAction());

            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
            if (!replaced) {
                queue.enqueueOrderedBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
        }

        returnActivityManager.BROADCAST_SUCCESS; }... }Copy the code
  1. Receivers are the list of static BroadcastReceivers that are interested in this broadcast; CollectReceiverComponents through PackageManager static BroadcastReceiver information obtained matching with the radio;
  2. The mReceiverResolver stores information about dynamically registered BroadcastReceiver.

Because in our code we use dynamic registration, receivers are null. The following two functions are called:

queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
Copy the code

The first function enlists the BroadcastRecord; The second function dispatches the broadcast. Queue is a BroadcastQueue object. We keep two broadcast queues and their associated bookkeeping, one for foreground priority and one for normal (background priority) broadcasts.

frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

public final class BroadcastQueue {...public void enqueueParallelBroadcastLocked(BroadcastRecord r) { mParallelBroadcasts.add(r); r.enqueueClockTime = System.currentTimeMillis(); }...public void scheduleBroadcastsLocked(a) {
        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

EnqueueParallelBroadcastLocked function is very simple, just add BroadcastRecord object r to the mParallelBroadcasts, MParallelBroadcasts represents a list of all active broadcasts that will be executed immediately (without waiting for another broadcast to complete). Currently, it contains only broadcasts to registered recipients, with background priority broadcasts and foreground priority broadcasts queued separately.

ScheduleBroadcastsLocked simply sends a message of type BROADCAST_INTENT_MSG through mHandler. The mBroadcastsScheduled flag indicates that we currently have an BROADCAST_INTENT_MSG message being processed.

frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

public final class BroadcastQueue {...final BroadcastHandler mHandler;

    private final class BroadcastHandler extends Handler {
        public BroadcastHandler(Looper looper) {
            super(looper, null.true);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BROADCAST_INTENT_MSG: {
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                    processNextBroadcast(true);
                } break; . }}}; . }Copy the code

BROADCAST_INTENT_MSG messages are finally processed by the handleMessage method in the BroadcastHandler class. The processNextBroadcast method is called next.

frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

public final class BroadcastQueue {...final void processNextBroadcast(boolean fromMsg) {
        synchronized(mService) {
            BroadcastRecord r;

            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
                    + mQueueName + "]."
                    + mParallelBroadcasts.size() + " broadcasts, "
                    + mOrderedBroadcasts.size() + " ordered broadcasts");

            mService.updateCpuStats();
            // Set mBroadcastsScheduled to false so that scheduleBroadcastsLocked does not return
            if (fromMsg) {
                mBroadcastsScheduled = false;
            }

            // First, any non-serialized broadcasts are immediately transmitted.
            while (mParallelBroadcasts.size() > 0) {
                r = mParallelBroadcasts.remove(0);
                r.dispatchTime = SystemClock.uptimeMillis();
                r.dispatchClockTime = System.currentTimeMillis();
                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);
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
                }
                addBroadcastToHistoryLocked(r);
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                        + mQueueName + "]"+ r); }... }}... }Copy the code

R.r eceivers is a List object, each of them BroadcastFilter object deliverToRegisteredReceiverLocked method is implemented.

frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

public final class BroadcastQueue {...private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered) {
        boolean skip = false; .if(! skip) {// If this is not sent as an ordered broadcast, then we do not want to touch the field that tracks the current state of the ordered broadcast.
            if (ordered) {
                ......
            }
            try {
                if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
                        "Delivering to " + filter + ":" + r);
                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); . }}}... }Copy the code

Next go to the performReceiveLocked method.

frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

public final class BroadcastQueue {...private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        // Use one-way binder calls to send the intent asynchronously to the receiver.
        if(app ! =null) {
            if(app.thread ! =null) {
                // If we have an application thread through which to execute the call so that it is properly sorted along with other one-way calls.
                app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                        data, extras, ordered, sticky, sendingUser, app.repProcState);
            } else {
                // The application is dead. The receiver does not exist.
                throw new RemoteException("app.thread must not be null"); }}else{ receiver.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser); }}... }Copy the code

App. Thread the end point is a ApplicationThreadProxy object, then call its scheduleRegisteredReceiver method.

frameworks/base/core/java/android/app/ApplicationThreadNative.java

class ApplicationThreadProxy implements IApplicationThread {...public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
            int resultCode, String dataStr, Bundle extras, boolean ordered,
            boolean sticky, int sendingUser, int processState) throws RemoteException {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IApplicationThread.descriptor);
        data.writeStrongBinder(receiver.asBinder());
        intent.writeToParcel(data, 0);
        data.writeInt(resultCode);
        data.writeString(dataStr);
        data.writeBundle(extras);
        data.writeInt(ordered ? 1 : 0);
        data.writeInt(sticky ? 1 : 0);
        data.writeInt(sendingUser);
        data.writeInt(processState);
        mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); }... }Copy the code

ApplicationThreadProxy scheduleRegisteredReceiver in the method, finally in the remote invocation ApplicationThread scheduleRegisteredReceiver method.

frameworks/base/core/java/android/app/ActivityThread.java

    private class ApplicationThread extends ApplicationThreadNative {...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

Take a look at the IIntentReceiver interface, which is defined in the AIDL file that we all know is used for cross-process communication.

frameworks/base/core/java/android/content/IIntentReceiver.aidl

package android.content;

import android.content.Intent;
import android.os.Bundle;

/** * System private API for sending Intent broadcasts. This is provided to the activity manager as part of registering an Intent broadcast and is called when it receives an Intent. * * {@hide} * /
oneway interface IIntentReceiver {
    void performReceive(in Intent intent, int resultCode, String data,
            in Bundle extras, boolean ordered, boolean sticky, int sendingUser);
}
Copy the code

Here is the Java code after compiling the AIDL, focusing on the queryLocalInterface method called in the asInterface method, which attempts to retrieve the local implementation of the interface for this Binder object. If null is returned, the proxy class needs to be instantiated to be called remotely through the Transact () method.

package android.content;

public interface IIntentReceiver extends android.os.IInterface {
    public void performReceive(android.content.Intent intent, int resultCode, java.lang.String data, android.os.Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws android.os.RemoteException;

    /** * Local IPC implementation stub class. * /
    public static abstract class Stub extends android.os.Binder implements android.content.IIntentReceiver {
        static final int TRANSACTION_performReceive = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        private static final java.lang.String DESCRIPTOR = "android.content.IIntentReceiver";

        /** * Constructs the Stub object and attaches it to the interface */
        public Stub(a) {
            this.attachInterface(this, DESCRIPTOR);
        }

        / * * * IBinder object casts for android. The content. IIntentReceiver interface, generated when there is a need for agents. * /
        public static android.content.IIntentReceiver asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if(((iin ! =null) && (iin instanceof android.content.IIntentReceiver))) {
                return ((android.content.IIntentReceiver) iin);
            }
            return new android.content.IIntentReceiver.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder(a) {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_performReceive: {
                    data.enforceInterface(descriptor);
                    android.content.Intent _arg0;
                    if ((0! = data.readInt())) { _arg0 = android.content.Intent.CREATOR.createFromParcel(data); }else {
                        _arg0 = null;
                    }
                    int _arg1;
                    _arg1 = data.readInt();
                    java.lang.String _arg2;
                    _arg2 = data.readString();
                    android.os.Bundle _arg3;
                    if ((0! = data.readInt())) { _arg3 = android.os.Bundle.CREATOR.createFromParcel(data); }else {
                        _arg3 = null;
                    }
                    boolean _arg4;
                    _arg4 = (0! = data.readInt());boolean _arg5;
                    _arg5 = (0! = data.readInt());int _arg6;
                    _arg6 = data.readInt();
                    this.performReceive(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags); }}}private static class Proxy implements android.content.IIntentReceiver {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder(a) {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor(a) {
                return DESCRIPTOR;
            }

            @Override
            public void performReceive(android.content.Intent intent, int resultCode, java.lang.String data, android.os.Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if((intent ! =null)) {
                        _data.writeInt(1);
                        intent.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    _data.writeInt(resultCode);
                    _data.writeString(data);
                    if((extras ! =null)) {
                        _data.writeInt(1);
                        extras.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    _data.writeInt(((ordered) ? (1) : (0)));
                    _data.writeInt(((sticky) ? (1) : (0)));
                    _data.writeInt(sendingUser);
                    mRemote.transact(Stub.TRANSACTION_performReceive, _data, null, android.os.IBinder.FLAG_ONEWAY);
                } finally {
                    _data.recycle();
                }
            }
        }
    }
}
Copy the code

Calling the performReceive method that implements the IIntentReceiver interface object is actually an abstract class that inherits from IIntentReceiver.Stub. As we learned in the Dynamic Registration broadcast section, we actually get an InnerReceiver object when we register, which is inherited from iIntentReceiver.stub. So it must end up executing its performReceive method.

frameworks/base/core/java/android/app/LoadedApk.java

public final class LoadedApk {...static final class ReceiverDispatcher {

        final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
            final LoadedApk.ReceiverDispatcher mStrongRef;

            InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                mStrongRef = strong ? rd : null;
            }
            public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                LoadedApk.ReceiverDispatcher 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 {
                    // The Activity manager distributes broadcasts to registered receivers during this process, but the receivers are not registered before the broadcasts are sent.
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing broadcast to unregistered receiver");
                    IActivityManager mgr = ActivityManagerNative.getDefault();
                    try {
                        if(extras ! =null) {
                            extras.setAllowFds(false);
                        }
                        mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
                    } catch (RemoteException e) {
                        Slog.w(ActivityThread.TAG, "Couldn't finish broadcast to unregistered receiver"); } } } } ...... }... }Copy the code

In InnerReceiver performReceive method, first from a weak reference LoadedApk. ReceiverDispatcher object, then call its performReceive method.

frameworks/base/core/java/android/app/LoadedApk.java

public final class LoadedApk {...static final class ReceiverDispatcher {...public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            if (ActivityThread.DEBUG_BROADCAST) {
                int seq = intent.getIntExtra("seq", -1);
                Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
                        + " to " + mReceiver);
            }
            Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
            if(! mActivityThread.post(args)) {if (mRegistered && ordered) {
                    IActivityManager mgr = ActivityManagerNative.getDefault();
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing sync broadcast to " + mReceiver);
                    args.sendFinished(mgr);
                }
            }
        }
    }
    ......
}
Copy the code

You first create an Args object, then use Handler to post the object, which implements the run method, and then execute its run method. Since we are sending a normal broadcast, it is out of order, ordered = false, and the internal if branch is no longer entered.

frameworks/base/core/java/android/app/LoadedApk.java

public final class LoadedApk {...static final class ReceiverDispatcher {...final class Args extends BroadcastReceiver.PendingResult implements Runnable {
            private Intent mCurIntent;
            private final boolean mOrdered;

            public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                    boolean ordered, boolean sticky, int sendingUser) {
                super(resultCode, resultData, resultExtras,
                        mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
                        sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags());
                mCurIntent = intent;
                mOrdered = ordered;
            }
            
            public void run(a) {
                finalBroadcastReceiver receiver = mReceiver; .final Intent intent = mCurIntent;
                mCurIntent = null; .try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    receiver.onReceive(mContext, intent);
                } catch(Exception e) { ...... }... }}... }... }Copy the code

BroadcastReceiver objects in the run method is when we registered the incoming, finally will carry out our custom Receiver the onReceive (MainActivityBroadcastReceiver) method.

Next, print a Log to trace the above call.

07-16 05:23:41. 244, 2323-2323 / com. The demo. The framework D/LHW: ContextWrapper sendBroadcast intent intent of = {act = com. Demo. Framework. The ACTION (from the extras)} 7-16 05:23:41. 244 2323-2323/com.demo.framework D/lhw: ContextImpl sendBroadcast intent intent of = {act = com. Demo. Framework. The ACTION (from the extras)} 7-16 05:23:41. 245 2323-2323/com.demo.framework V/lhw: ActivityManagerProxy broadcastIntent caller=android.app.ActivityThread$ApplicationThread@4ad6d55,intent=Intent { act=com.demo.framework.ACTION (has extras) },resolvedType=null,resultTo=null,resultCode=-1,resultData=null,map=null,appOp=-1,options=null,serialized=false,sticky=f Alse,userId=0 07-16 05:23:41.245 /system_process V/ LHW: ActivityManagerService broadcastIntent caller=android.app.ApplicationThreadProxy@363d099,intent=Intent { act=com.demo.framework.ACTION (has extras) },resolvedType=null,resultTo=null,resultCode=-1,resultData=null,resultExtras=null,appOp=-1,options=null,serialized=false ,sticky=false,userId=0 07-16 05:23:41.245 /system_process V/ LHW: ActivityManagerService broadcastIntentLocked callerApp=ProcessRecord{a649f5e 2323:com.demo.framework/u0a124},callerPackage=com.demo.framework,intent=Intent { act=com.demo.framework.ACTION (has extras) },resolvedType=null,resultTo=null,resultCode=-1,resultData=null,resultExtras=null,appOp=-1,options=null,ordered=false,st Icky =false,callingPid=2323,callingUid=10124,userId=0 07-16 05:23:41.245 /system_process V/ LHW: ActivityManagerService broadcastIntentLocked replace =false 07-16 05:23:41.245 770-1644/system_process V/ LHW: BroadcastQueue enqueueParallelBroadcastLocked r = BroadcastRecord {61 aaf3f u0 com. The demo. The framework. The ACTION} 7-16 05:23:41. 245  770-1644/system_process V/lhw: BroadcastQueue scheduleBroadcastsLocked 07-16 05:23:41.245 770-803/system_process V/ LHW: BroadcastHandler handleMessage BROADCAST_INTENT_MSG 07-16 05:23:41.245 770-803/system_process V/ LHW: BroadcastQueue processNextBroadcast fromMsg=true 07-16 BroadcastQueue processNextBroadcast fromMsg=true 07-16 05:23:41.245 770-803/system_process V/ LHW: BroadcastQueue deliverToRegisteredReceiverLocked r=BroadcastRecord{61aaf3f u0 com.demo.framework.ACTION},filter=BroadcastFilter{5c0e7dc u0 ReceiverList{c66144f 2323 com.demo.framework/10124/u0 Remote :4792eae}},ordered=false 07-16 05:23:41. 7770-803 /system_process V/ LHW: BroadcastQueue performReceiveLocked app=ProcessRecord{a649f5e 2323:com.demo.framework/u0a124},receiver=android.content.IIntentReceiver$Stub$Proxy@87d70b0,intent=Intent { act=com.demo.framework.ACTION flg=0x10 (has extras) }, the resultCode = 1, data = null, extras = null, ordered = false, sticky = false, sendingUser = 0 07-16 05:23:41. 245, 770-803 / system_process  V/lhw: ApplicationThreadProxy scheduleRegisteredReceiver receiver=android.content.IIntentReceiver$Stub$Proxy@87d70b0,intent=Intent { act=com.demo.framework.ACTION flg=0x10 (has extras) },resultCode=-1,dataStr=null,extras=null,ordered=false,sticky=false,sendingUser=0,processState=2 07-16 05:23:41. 245, 2323-2341 / com. Demo. The framework V/LHW: ApplicationThread scheduleRegisteredReceiver receiver=android.app.LoadedApk$ReceiverDispatcher$InnerReceiver@9e5a36a,intent=Intent { act=com.demo.framework.ACTION flg=0x10 (has extras) },resultCode=-1,dataStr=null,extras=null,ordered=false,sticky=false,sendingUser=0,processState=2 07-16 05:23:41. 246, 2323-2341 / com. The demo. The framework V/LHW: InnerReceiver performReceive intent=Intent { act=com.demo.framework.ACTION flg=0x10 (has extras) }, the resultCode = 1, datanull, extras = null, ordered = false, sticky = false, sendingUser = 0 07-16 05:23:41. 246 2323-2341/com.demo.framework V/lhw: ReceiverDispatcher performReceive intent=Intent { act=com.demo.framework.ACTION flg=0x10 (has extras) }, the resultCode = 1, datanull, extras = null, ordered = false, sticky = false, sendingUser = 0 07-16 05:23:41. 248 2323-2323/com.demo.framework V/lhw: Args run ordered=false,mContext=com.demo.framework.MainActivity@bf19c05,intent=Intent { act=com.demo.framework.ACTION FLG =0x10 (has extras)} 07-16 05:23:41.252 2323-2323/com.demo. Framework D/MainActivity: MainActivityBroadcastReceiver onReceive str=DemoService Hello!Copy the code

To conclude, draw a sequence diagram.