美文网首页
跨进程广播的工作原理(2)

跨进程广播的工作原理(2)

作者: snake_6d77 | 来源:发表于2019-08-08 09:57 被阅读0次

    广播的发送主流程

    上一篇文章我们分析了registerReceiver方法的详细实现,这篇将继续往下分析发送广播的具体实现

    sendBroadcast方法干了什么?

    应用进程中我们调用Context.sendBroadcast(Intent intent)方法最终也是走到ContextImpl的sendBroadcast(Intent intent) 实现方法中:

        @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) {
                throw e.rethrowFromSystemServer();
            }
        }
    

    我们发现这个方法其实没做什么重要的事情,重要的事情最终还是要在系统服务中处理!所以我们又要跑去看ActivityManagerService类的代码了,然后你会发现发送广播的代码还真挺长的,但好在里面的英文注释还挺详细的:

        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);
                //清除IPC通道内的进程id
                Binder.restoreCallingIdentity(origId);
                return res;
            }
        }
    
        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) {
            intent = new Intent(intent);
            //Instant App特殊处理
            final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
            // Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
            if (callerInstantApp) {
                intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
            }
    
            // By default broadcasts do not go to stopped apps.默认广播不会停止app
            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);
            }
    
            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                    (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
                    + " ordered=" + ordered + " userid=" + userId);
            if ((resultTo != null) && !ordered) {
                Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
            }
            //获取广播目的用户id
            userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                    ALLOW_NON_FULL, "broadcast", callerPackage);
    
            // Make sure that the user who is receiving this broadcast is running.
            // If not, we will just skip it. Make an exception for shutdown broadcasts
            // and upgrade steps.确保目标用户存在
            if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) {
                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 + " is stopped");
                    return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
                }
            }
    
            BroadcastOptions brOptions = null;
            if (bOptions != null) {
                brOptions = new BroadcastOptions(bOptions);
                if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
                    // See if the caller is allowed to do this.  Note we are checking against
                    // the actual real caller (not whoever provided the operation as say a
                    // PendingIntent), because that who is actually supplied the arguments.
                    //判断发送广播的进程是否有相关权限
                    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 new SecurityException(msg);
                    }
                }
            }
    
            // Verify that protected broadcasts are only being sent by system code,
            // and that system code is only sending protected broadcasts.
            //部分被保护的广播只有系统才能发,其它应用发出都会被拦截
            final String action = intent.getAction();
            final boolean isProtectedBroadcast;
            try {
                isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
            } catch (RemoteException e) {
                Slog.w(TAG, "Remote exception", e);
                return ActivityManager.BROADCAST_SUCCESS;
            }
            //判断是不是系统发起的广播
            final boolean isCallerSystem;
            switch (UserHandle.getAppId(callingUid)) {
                case ROOT_UID:
                case SYSTEM_UID:
                case PHONE_UID:
                case BLUETOOTH_UID:
                case NFC_UID:
                    isCallerSystem = true;
                    break;
                default:
                    isCallerSystem = (callerApp != null) && callerApp.persistent;
                    break;
            }
    
            // First line security check before anything else: stop non-system apps from
            // sending protected broadcasts.
            //不合理广播抛出异常
            if (!isCallerSystem) {
                if (isProtectedBroadcast) {
                    String msg = "Permission Denial: not allowed to send broadcast "
                            + action + " from pid="
                            + callingPid + ", uid=" + callingUid;
                    Slog.w(TAG, msg);
                    throw new SecurityException(msg);
    
                } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                        || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
                    // Special case for compatibility: we don't want apps to send this,
                    // but historically it has not been protected and apps may be using it
                    // to poke their own app widget.  So, instead of making it protected,
                    // just limit it to the caller.
                    if (callerPackage == null) {
                        String msg = "Permission Denial: not allowed to send broadcast "
                                + action + " from unknown caller.";
                        Slog.w(TAG, msg);
                        throw new SecurityException(msg);
                    } else if (intent.getComponent() != null) {
                        // They are good enough to send to an explicit component...  verify
                        // it is being sent to the calling app.
                        if (!intent.getComponent().getPackageName().equals(
                                callerPackage)) {
                            String msg = "Permission Denial: not allowed to send broadcast "
                                    + action + " to "
                                    + intent.getComponent().getPackageName() + " from "
                                    + callerPackage;
                            Slog.w(TAG, msg);
                            throw new SecurityException(msg);
                        }
                    } else {
                        // Limit broadcast to their own package.
                        intent.setPackage(callerPackage);
                    }
                }
            }
            //对系统定义的action进行相应处理
            if (action != null) {
                if (getBackgroundLaunchBroadcasts().contains(action)) {
                    if (DEBUG_BACKGROUND_CHECK) {
                        Slog.i(TAG, "Broadcast action " + action + " forcing include-background");
                    }
                    intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                }
    
                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:
                    case Intent.ACTION_PACKAGES_SUSPENDED:
                    case Intent.ACTION_PACKAGES_UNSUSPENDED:
                        // Handle special intents: if this broadcast is from the package
                        // manager about a package being removed, we need to remove all of
                        // its activities from the history stack.
                        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 int uid = getUidFromIntent(intent);
                                if (uid >= 0) {
                                    mBatteryStatsService.removeUid(uid);
                                    mAppOpsService.uidRemoved(uid);
                                }
                                break;
                            case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
                                // If resources are unavailable just force stop all those packages
                                // and flush the attribute cache as well.
                                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(
                                            ApplicationThreadConstants.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);
                                    final boolean replacing =
                                            intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
                                    final boolean killProcess =
                                            !intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);
                                    final boolean fullUninstall = removed && !replacing;
                                    if (removed) {
                                        if (killProcess) {
                                            forceStopPackageLocked(ssp, UserHandle.getAppId(
                                                    intent.getIntExtra(Intent.EXTRA_UID, -1)),
                                                    false, true, true, false, fullUninstall, userId,
                                                    removed ? "pkg removed" : "pkg changed");
                                        }
                                        final int cmd = killProcess
                                                ? ApplicationThreadConstants.PACKAGE_REMOVED
                                                : ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL;
                                        sendPackageBroadcastLocked(cmd,
                                                new String[] {ssp}, userId);
                                        if (fullUninstall) {
                                            mAppOpsService.packageRemoved(
                                                    intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);
    
                                            // Remove all permissions granted from/to this package
                                            removeUriPermissionsForPackageLocked(ssp, userId, true);
    
                                            removeTasksByPackageNameLocked(ssp, userId);
    
                                            mServices.forceStopPackageLocked(ssp, userId);
    
                                            // Hide the "unsupported display" dialog if necessary.
                                            if (mUnsupportedDisplaySizeDialog != null && ssp.equals(
                                                    mUnsupportedDisplaySizeDialog.getPackageName())) {
                                                mUnsupportedDisplaySizeDialog.dismiss();
                                                mUnsupportedDisplaySizeDialog = null;
                                            }
                                            mCompatModePackages.handlePackageUninstalledLocked(ssp);
                                            mBatteryStatsService.notePackageUninstalled(ssp);
                                        }
                                    } else {
                                        if (killProcess) {
                                            killPackageProcessesLocked(ssp, UserHandle.getAppId(
                                                    intent.getIntExtra(Intent.EXTRA_UID, -1)),
                                                    userId, ProcessList.INVALID_ADJ,
                                                    false, true, true, false, "change " + ssp);
                                        }
                                        cleanupDisabledPackageComponentsLocked(ssp, userId, killProcess,
                                                intent.getStringArrayExtra(
                                                        Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST));
                                    }
                                }
                                break;
                            case Intent.ACTION_PACKAGES_SUSPENDED:
                            case Intent.ACTION_PACKAGES_UNSUSPENDED:
                                final boolean suspended = Intent.ACTION_PACKAGES_SUSPENDED.equals(
                                        intent.getAction());
                                final String[] packageNames = intent.getStringArrayExtra(
                                        Intent.EXTRA_CHANGED_PACKAGE_LIST);
                                final int userHandle = intent.getIntExtra(
                                        Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
    
                                synchronized(ActivityManagerService.this) {
                                    mRecentTasks.onPackagesSuspendedChanged(
                                            packageNames, suspended, userHandle);
                                }
                                break;
                        }
                        break;
                    case Intent.ACTION_PACKAGE_REPLACED:
                    {
                        final Uri data = intent.getData();
                        final String ssp;
                        if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
                            ApplicationInfo aInfo = null;
                            try {
                                aInfo = AppGlobals.getPackageManager()
                                        .getApplicationInfo(ssp, 0 /*flags*/, userId);
                            } catch (RemoteException ignore) {}
                            if (aInfo == null) {
                                Slog.w(TAG, "Dropping ACTION_PACKAGE_REPLACED for non-existent pkg:"
                                        + " ssp=" + ssp + " data=" + data);
                                return ActivityManager.BROADCAST_SUCCESS;
                            }
                            mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
                            sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
                                    new String[] {ssp}, userId);
                        }
                        break;
                    }
                    case Intent.ACTION_PACKAGE_ADDED:
                    {
                        // Special case for adding a package: by default turn on compatibility mode.
                        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_PACKAGE_DATA_CLEARED:
                    {
                        Uri data = intent.getData();
                        String ssp;
                        if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
                            // Hide the "unsupported display" dialog if necessary.
                            if (mUnsupportedDisplaySizeDialog != null && ssp.equals(
                                    mUnsupportedDisplaySizeDialog.getPackageName())) {
                                mUnsupportedDisplaySizeDialog.dismiss();
                                mUnsupportedDisplaySizeDialog = null;
                            }
                            mCompatModePackages.handlePackageDataClearedLocked(ssp);
                        }
                        break;
                    }
                    case Intent.ACTION_TIMEZONE_CHANGED:
                        // If this is the time zone changed action, queue up a message that will reset
                        // the timezone of all currently running processes. This message will get
                        // queued up before the broadcast happens.
                        mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
                        break;
                    case Intent.ACTION_TIME_CHANGED:
                        // EXTRA_TIME_PREF_24_HOUR_FORMAT is optional so we must distinguish between
                        // the tri-state value it may contain and "unknown".
                        // For convenience we re-use the Intent extra values.
                        final int NO_EXTRA_VALUE_FOUND = -1;
                        final int timeFormatPreferenceMsgValue = intent.getIntExtra(
                                Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT,
                                NO_EXTRA_VALUE_FOUND /* defaultValue */);
                        // Only send a message if the time preference is available.
                        if (timeFormatPreferenceMsgValue != NO_EXTRA_VALUE_FOUND) {
                            Message updateTimePreferenceMsg =
                                    mHandler.obtainMessage(UPDATE_TIME_PREFERENCE_MSG,
                                            timeFormatPreferenceMsgValue, 0);
                            mHandler.sendMessage(updateTimePreferenceMsg);
                        }
                        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;
                    case android.hardware.Camera.ACTION_NEW_PICTURE:
                    case android.hardware.Camera.ACTION_NEW_VIDEO:
                        // In N we just turned these off; in O we are turing them back on partly,
                        // only for registered receivers.  This will still address the main problem
                        // (a spam of apps waking up when a picture is taken putting significant
                        // memory pressure on the system at a bad point), while still allowing apps
                        // that are already actively running to know about this happening.
                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                        break;
                    case android.security.KeyChain.ACTION_TRUST_STORE_CHANGED:
                        mHandler.sendEmptyMessage(HANDLE_TRUST_STORAGE_UPDATE_MSG);
                        break;
                    case "com.android.launcher.action.INSTALL_SHORTCUT":
                        // As of O, we no longer support this broadcasts, even for pre-O apps.
                        // Apps should now be using ShortcutManager.pinRequestShortcut().
                        Log.w(TAG, "Broadcast " + action
                                + " no longer supported. It will not be delivered.");
                        return ActivityManager.BROADCAST_SUCCESS;
                }
    
                if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
                        Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
                        Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
                    final int uid = getUidFromIntent(intent);
                    if (uid != -1) {
                        final UidRecord uidRec = mActiveUids.get(uid);
                        if (uidRec != null) {
                            uidRec.updateHasInternetPermission();
                        }
                    }
                }
            }
    
            // Add to the sticky list if requested.粘性广播特殊处理
            if (sticky) {
                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, 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(
                            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 sticky already exists, replace it.
                        list.set(i, new Intent(intent));
                        break;
                    }
                }
                if (i >= stickiesCount) {
                    list.add(new Intent(intent));
                }
            }
            //根据广播目标用户获取相应的用户数组
            int[] users;
            if (userId == UserHandle.USER_ALL) {
                // Caller wants broadcast to go to all started users.
                users = mUserController.getStartedUserArrayLocked();
            } else {
                // Caller wants broadcast to go to one specific user.
                users = new int[] {userId};
            }
    
            // Figure out who all will receive this broadcast.找出谁将接收这个广播
            //此处暂时不深究是如何找到目标广播接收者的(后面重点分析),先走完主流程
            List receivers = null;
            List<BroadcastFilter> registeredReceivers = null;
            // Need to resolve the intent to interested receivers...
            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 == SHELL_UID) {
                    // Query one target user at a time, excluding shell-restricted users
                    for (int i = 0; i < users.length; i++) {
                        if (mUserController.hasUserRestriction(
                                UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                            continue;
                        }
                        List<BroadcastFilter> registeredReceiversForUser =
                                mReceiverResolver.queryIntent(intent,
                                        resolvedType, false /*defaultOnly*/, users[i]);
                        if (registeredReceivers == null) {
                            registeredReceivers = registeredReceiversForUser;
                        } else if (registeredReceiversForUser != null) {
                            registeredReceivers.addAll(registeredReceiversForUser);
                        }
                    }
                } else {
                    registeredReceivers = mReceiverResolver.queryIntent(intent,
                            resolvedType, false /*defaultOnly*/, userId);
                }
            }
    
            final boolean replacePending =
                    (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
    
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
                    + " replacePending=" + replacePending);
    
            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);
                }
                //根据Intent的FLAG来获取前台广播或后台广播队列
                final BroadcastQueue queue = broadcastQueueForIntent(intent);
                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) {
                    queue.enqueueParallelBroadcastLocked(r);
                    //此方法里面一顿操作,然后把广播发出去了(后续详细分析内部实现)
                    queue.scheduleBroadcastsLocked();
                }
                registeredReceivers = null;
                NR = 0;
            }
    
            // Merge into one list.
            int ir = 0;
            if (receivers != null) {
                // A special case for PACKAGE_ADDED: do not allow the package
                // being added to see this broadcast.  This prevents them from
                // using this as a back door to get run as soon as they are
                // installed.  Maybe in the future we want to have a special install
                // broadcast or such for apps, but we'd like to deliberately make
                // this decision.
                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 = new String[] { 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--;
                                }
                            }
                        }
                    }
                }
                //将registeredReceivers内的BroadcastFilter并入到receivers中,并按优先级排序
                int NT = 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将作为最终使用的集合
                        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++;
            }
    
            if (isCallerSystem) {
                checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                        isProtectedBroadcast, receivers);
            }
            //最后的广播分发逻辑
            if ((receivers != null && receivers.size() > 0)
                    || resultTo != null) {
                BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                        callerPackage, callingPid, callingUid, callerInstantApp, 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());
                //寻找已有的广播记录
                final BroadcastRecord oldRecord =
                        replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
                if (oldRecord != null) {
                    // Replaced, fire the result-to receiver.,替换旧的记录,触发广播发送
                    if (oldRecord.resultTo != null) {
                        final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
                        try {
                            //回调应用进程的ApplicationThread接口,实现广播发送到进程
                            oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
                                    oldRecord.intent,
                                    Activity.RESULT_CANCELED, null, null,
                                    false, false, oldRecord.userId);
                        } catch (RemoteException e) {
                            Slog.w(TAG, "Failure ["
                                    + queue.mQueueName + "] sending broadcast result of "
                                    + intent, e);
    
                        }
                    }
                } else {
                    //把广播记录r添加进有序广播队列
                    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);
                }
            }
    
            return ActivityManager.BROADCAST_SUCCESS;
        }
    
    

    阅读完上面的源码后,我们整理一下里面究竟实现了什么:
    1、花了一些篇幅对广播意图的合理性做了判断,同时也处理了一些系统广播意图。
    2、查找广播意图对应的广播接收者,并将广播意图发送到相应进程。这里面包含了对有序广播、粘性广播等的一些特殊处理。
    3、上面关于用广播Intent查找对应广播接收者的逻辑,以及广播Intent分发的具体逻辑待后续继续深入研读。

    相关文章

      网友评论

          本文标题:跨进程广播的工作原理(2)

          本文链接:https://www.haomeiwen.com/subject/sshsfctx.html