FrameWork层源码解析(4)-广播发送过程源码解析

主目录见:Android高级进阶知识(这是总目录索引)

 上一篇文章注册广播接收者的源码分析我们已经讲了注册的过程,我们知道动态广播注册对应的在AMS中的节点是BroadcastFilter,静态广播是在PMS中对应的是ResolveInfo,我们这篇文章将讲另外一个过程,那就是广播的发送过程,这一过程肯定就是发送到AMS中,然后AMS查找自己注册的广播中有没有这个广播,如果有则会发送给订阅了该广播的所有接收者。

一.目标

今天介绍这篇也是跟广播相关的,其实这个过程除了多一些跨进程通讯之外,其他跟事件总线还是有点像的,至少思想类似。今天目标如下:
1.了解广播的整体流程;
2.为插件化框架做知识储备。

二.源码解析

如果看过前面源码分析的应该知道,我们的调用最终都会到达ContextImpl类中,广播也不例外,我们看到这个类中的sendBroadcast()方法:

  @Override
    public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        //resolvedType表示intent的MIME类型
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            //准备离开应用程序进程
            intent.prepareToLeaveProcess(this);
            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 e.rethrowFromSystemServer();
        }
    }

我们看到这里ActivityManagerNative#getDefault()获取到的是远程服务的代理,所以我们直接看到AMS(ActivityManagerService)broadcastIntent()方法:

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);
            //从mLruProcesses集合中查找进程记录
            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, appOp, bOptions, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

我们看到程序会调用broadcastIntentLocked()方法继续处理,接下来这个方法巨长,我们精简地看:

  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);

        //1.设置这个标志说明不会发送给已经停止的app
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

        //如果你还没完成启动,那么不允许创建一个新的进程
        if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
            //这个标志说明只有静态注册广播能接受到广播
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        }

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

        // Make sure that the user who is receiving this broadcast 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 != Process.SYSTEM_UID
                    || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
                    && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
                return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
            }
        }

      //2.检查BroadcastOptions
       BroadcastOptions brOptions = null;
        if (bOptions != null) {
            brOptions = new BroadcastOptions(bOptions);
            //获取发送广播时候,系统把cpu暂时唤醒的时间
            if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
                //是否有添加这个应用到活动应用白名单中的权限
                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);
                }
            }
        }

        //3.检查是否有权发送广播
         final String action = intent.getAction();
        final boolean isProtectedBroadcast;
        try {
            //到PMS中查找是否isProtectedBroadcast 是true,
            //即是否在Framework/base/core/res/AndroidManifest.xml中
            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 Process.ROOT_UID:
            case Process.SYSTEM_UID:
            case Process.PHONE_UID:
            case Process.BLUETOOTH_UID:
            case Process.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) {
              //如果不是系统进程调用,但是又在Framework/base/core/res/AndroidManifest.xml中,
              //这种情况基本没有,如果存在那就是不允许的
                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)) {
              //这是历史遗留原因,系统不建议我们发送这两类广播
                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) {
                    //如果发送者的包名和接收者不在一个包名下面则禁止发送
                    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);
                }
            }
        }
      //4.省略一些系统相关广播的处理
      ......

      //5.处理粘性广播
      //6.动态广播和静态广播注册表的查询
}

从上面代码我们可以看出,我们先列出了4个步骤的讲解,首先1.标志的设置,2.检查BroadcastOptions,3.检查是否有权发送广播,4.省略一些系统相关广播的处理。然后我们还有两个步骤未讲解,我们下面分开来讲。

1.处理粘性广播

        // Add to the sticky list if requested.
        if (sticky) {
            //检查是否有android.Manifest.permission.BROADCAST_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");
            }

          //如果用户id不是USER_ALL
            if (userId != UserHandle.USER_ALL) {
                //首先取出所有USER_ALL对应的粘性广播,所以为了确保不会跟发送给所有用户的广播冲突,
              //这里会进行检查
                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
                        UserHandle.USER_ALL);
                if (stickies != null) {
                    //从发送给所有用户的粘性广播中查找是否有跟发送的intent对应的广播
                    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");
                            }
                        }
                    }
                }
            }

            //如果上面检查过 不冲突,则从粘性广播集合中根据这个用户id取与之对应的广播
            ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
            if (stickies == null) {
                //因为粘性广播发送过会保存,这里就是保存代码
                stickies = new ArrayMap<>();
                mStickyBroadcasts.put(userId, stickies);
            }
          //当前用户的所有粘性广播中查找是否有intent的action相同的广播
            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.
                    // 新发送的intent在ArrayList中已经有个“相等的”旧intent时,则会用新的替掉旧的
                    list.set(i, new Intent(intent));
                    break;
                }
            }
            if (i >= stickiesCount) {
                list.add(new Intent(intent));
            }
        }

我们看到粘性广播的话那么sticky为true,方法首先会检查是否有发送粘性广播的权限。然后会看发送的粘性广播和已经发送的广播是否冲突(当不是发送给所有用户的情况下)。而且我们知道发送过的广播是放在mStickyBroadcasts集合中的,我们发送广播首先要根据用户id查找,然后再从查找的结果中查找与发送intent中action相同的广播。当然粘性广播也是在注册广播的时候添加进来的。接下来我们看看我们的步骤6。

6.动态广播和静态广播注册表的查询

  //查找谁会接受这个广播
        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
            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 shell-restricted users
                for (int i = 0; i < users.length; i++) {
                    if (mUserController.hasUserRestriction(
                            UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                        continue;
                    }
                    //查找所有用户下面的跟这个intent对应的广播接收者,因为接收者可能有多个,
                    //所以结果放置在List中
                    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);
            }
        }

我们看到这里会先查找静态注册的广播看有没有,有的话就会赋值给receivers,然后会查找动态注册的广播,有的话会赋值给registeredReceivers,我们接着往下看:

 //如果设置了的话,ActivityManagerService就会在当前的系统中查看有没有相同的intent还未被处理,
//如果有的话,就有当前这个新的intent来替换旧的intent。
  final boolean replacePending =
                (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueing broadcast: " + intent.getAction()
                + " replacePending=" + replacePending);
      //得到查找到的动态注册的广播数量
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        if (!ordered && NR > 0) {//如果发送的是无序广播,且查找到的动态注册的广播数量大于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放入广播队列BroadcastQueue 中
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
           //构建一个广播实体广播实体类
            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);
            final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
            if (!replaced) {
               //加入到并行广播队列
                queue.enqueueParallelBroadcastLocked(r);
                //处理并行队列中的广播
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }

这个方法就是首先从动态注册广播的集合中查找对应的广播的数量,然后查找到有接收者,那么就将intent封装到BroadcastRecord中,然后放入并行广播队列中,然后调用scheduleBroadcastsLocked()方法进行处理。这里的处理我们等会再说,我们先继续往下看代码:

   // Merge into one list.
        int ir = 0;
        if (receivers != null) {
            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--;
                            }
                        }
                    }
                }
            }

            int NT = receivers != null ? receivers.size() : 0;
            int it = 0;
            ResolveInfo curt = null;
            BroadcastFilter curr = null;
            while (it < NT && ir < NR) {
                if (curt == null) {
                    //静态注册的广播是ResolveInfo类型 
                    curt = (ResolveInfo)receivers.get(it);
                }
                if (curr == null) {
                    //动态注册的广播是BroadcastFilter类型
                    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;
                }
            }
        }

        //把优先级低于所有静态注册广播接收者的动态广播接收者都追加到receivers列表中的末尾  
        while (ir < NR) {
            if (receivers == null) {
                receivers = new ArrayList();
            }
            receivers.add(registeredReceivers.get(ir));
            ir++;
        }

我们看到这个方法主要就是做一件事情,就是合并动态注册广播列表和静态注册广播列表,根据优先级来决定排的前后,同样优先级的情况下,动态注册的广播要排在静态注册广播的前面。最后我们看下处理有序广播的过程:

  if ((receivers != null && receivers.size() > 0)
                || resultTo != null) {
            //根据intent查找到广播队列
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            //创建广播实体BroadcastRecord 
            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();
            }
        } 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);
            }
        }

这代码跟上面动态注册广播加入到并行广播队列类似,接着就看广播队列中广播的处理过程了,我们知道我们添加到队列中是要拿出来处理的。

7.广播队列中广播的处理

我们从上文知道,我们广播队列的处理是在BroadcastQueue#scheduleBroadcastsLocked()方法进行处理的:

 public void scheduleBroadcastsLocked() {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                + mQueueName + "]: current="
                + mBroadcastsScheduled);
        //表示ActivityManagerService当前是不是正在处理其它广播,如果是的话,这里就先不处理直接返回了,保证所有广播串行处理
        if (mBroadcastsScheduled) {
            return;
        }
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }

我们看到这个方法主要是发送一个带有BROADCAST_INTENT_MSG标志的消息,这里的mHandlerBroadcastQueue的内部类:

    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;
                case BROADCAST_TIMEOUT_MSG: {
                 .......
                } break;
                case SCHEDULE_TEMP_WHITELIST_MSG: {
                 .......
                } break;
            }
        }
    }

我们看到这里的逻辑主要交给了processNextBroadcast()方法处理,这个方法很长,这一篇讲完的话篇幅太长了,那我们留着下一篇继续讲解吧。

总结:我们看到广播的查找过程还是条件很多的,但是总的来说还是比较清晰的,看的过程中希望大家不要急躁,细细地品味。祝大家旅程愉快,最后如果有什么错误或者建议大家可以提出哈。

    原文作者:ZJ_Rocky
    原文地址: https://www.jianshu.com/p/013f844d0961
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞