Broadcast发送流程

文中的源代码版本为api23

Context提供了多种方法来发送广播,sendBroadcastsendOrderedBroadcastsendStickyBroadcastsendStickyOrderedBroadcast等等,最终都是调用的ActivityManagerService.broadcastIntent。 我们所熟知的有序广播(ordered)和粘性广播(sticky),只不过是对应ActivityManagerService.broadcastIntent方法的其中两个boolean类型的形参而已。 由于ContextImpl中的各种send逻辑都比较简单,ActivityManagerService.broadcastIntent方法直接调用了ActivityManagerService.broadcastIntentLocked方法,所以我们直接从ActivityManagerService.broadcastIntentLocked方法开始分析。

1. ActivityManagerService.broadcastIntentLocked方法

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

    //...一些多用户场景的处理,非主流程,忽略

    /*
     * Prevent non-system code (defined here to be non-persistent
     * processes) from sending protected broadcasts.
     * 不允许非系统代码发送受保护的广播
     */
    //权限检查,某些广播是不允许非系统app发送的
    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) {
        // 这些appId都是大佬,什么广播都给发...
    } else if (callerApp == null || !callerApp.persistent) {
        try {
            if (AppGlobals.getPackageManager().isProtectedBroadcast(
                    intent.getAction())) {
                //通过PMS来判断当前广播是否是受保护的广播
                //如果是,则抛出安全异常
                throw new SecurityException(msg);
            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(intent.getAction())) {
                if (callerApp == null) {
                    //...
                    throw new SecurityException(msg);
                } else if (intent.getComponent() != null) {
                    if (!intent.getComponent().getPackageName().equals(
                            callerApp.info.packageName)) {
                        //...
                        throw new SecurityException(msg);
                    }
                } else {
                    // Limit broadcast to their own package.
                    intent.setPackage(callerApp.info.packageName);
                }
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "Remote exception", e);
            return ActivityManager.BROADCAST_SUCCESS;
        }
    }

    //内置一些action的处理
    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:
                //...应用发生卸载等情况,处理Activity栈等等逻辑
                break;
            case Intent.ACTION_PACKAGE_ADDED:
                //...新增应用,启动兼容模式
                break;
            case Intent.ACTION_TIMEZONE_CHANGED:
                //处理时区变化
                mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
                break;
            case Intent.ACTION_TIME_CHANGED:
                //用户重新设置了时间,通知所有app,最终会触发ApplicationThread.updateTimePrefs方法
                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;
        }
    }

    //如果是粘性广播,则保存广播至mStickyBroadcasts
    if (sticky) {
        //安全检查
        
        //发送粘性广播需要有BROADCAST_STICKY权限
        if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
                callingPid, callingUid)
                != PackageManager.PERMISSION_GRANTED) {
            //...
            throw new SecurityException(msg);
        }
        
        //粘性广播不允许有requiredPermissions参数
        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");
        }
        //检测是否与已有的全局广播(userId为USER_ALL,可发送给所有的用户)冲突
        if (userId != UserHandle.USER_ALL) {
            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");
                        }
                    }
                }
            }
        }
        
        //保存粘性广播
        //mStickyBroadcasts类型为SparseArray<ArrayMap<String, ArrayList<Intent>>>
        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
        //...
        ArrayList<Intent> list = stickies.get(intent.getAction());
        //...
        final int stickiesCount = list.size();
        int i;
        for (i = 0; i < stickiesCount; i++) {
            if (intent.filterEquals(list.get(i))) {
                // 存在相同的广播,就进行替换
                list.set(i, new Intent(intent));
                break;
            }
        }
        if (i >= stickiesCount) {
            //新增广播
            list.add(new Intent(intent));
        }
    }

    //...

    // receivers保存静态注册的广播接收者
    List receivers = null;
    // registeredReceivers保存动态注册的广播接收者
    List<BroadcastFilter> registeredReceivers = null;
    // Intent默认不带FLAG_RECEIVER_REGISTERED_ONLY标志
    if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY)
            == 0) {
        //从PMS中解析出那些静态注册的Receiver
        //内部会调用PMS.queryIntentReceivers方法检索匹配的广播接收者
        //该函数内部还会处理primaryUserOnly和singleUser属性
        //同时还会将接收者按照优先级进行排序返回
        //该函数逻辑不复杂,就不再展开讲了
        receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
    }
    if (intent.getComponent() == null) {
        if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
            //...通常会走下面这个分支
        } else {
            // mReceiverResolver还记得么?在上面注册广播的流程中,AMS会生成BroadcastFilter
            // 保存在mReceiverResolver中。此处的queryIntent就是通过Intent取出
            // 匹配的BroadcastFilter。返回的列表也是经过优先级排序的
            // queryIntent的实现细节就不讨论了,有兴趣大家可以自行分析
            registeredReceivers = mReceiverResolver.queryIntent(intent,
                    resolvedType, false, userId);
        }
    }
    
    //至此AMS已经找到了所有监听了该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) {
        //无序广播只涉及到registeredReceivers(也即是动态广播接收者)
    
        //AMS中有两个BroadcastQueue:mFgBroadcastQueue、mBgBroadcastQueue
        //broadcastQueueForIntent通过判断Intent中的FLAG_RECEIVER_FOREGROUND标志
        //来决定使用哪个Queue
        final BroadcastQueue queue = broadcastQueueForIntent(intent);
        //将所有信息封装成一个BroadcastRecord
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
                appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
                resultExtras, ordered, sticky, false, userId);
        //replaceParallelBroadcastLocked方法内部会替换已有的BroadcastRecord
        //判定条件是Intent.filterEquals,替换成功返回true,否则返回false
        //replacePending与FLAG_RECEIVER_REPLACE_PENDING标志有关,该标志通常与
        //粘性广播一起使用,用来更新现有的粘性广播
        //
        final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
        if (!replaced) {
            //将BroadcastRecord插入到广播列表
            queue.enqueueParallelBroadcastLocked(r);
            //开始发送广播
            queue.scheduleBroadcastsLocked();
        }
        
        //划重点!!
        //无序广播发送完毕之后会置空registeredReceivers
        //对下面发送有序广播会产生影响
        registeredReceivers = null;
        NR = 0;
    }
   
    //无序广播发完之后没有直接return,而是继续发送有序广播,是不是很奇怪?

    //有序广播
    int ir = 0;
    //按照优先级合并静态广播接收者和动态广播接收者
    if (receivers != null) {
        //将某些特殊的Receiver从接受列表中删除,比如在安装成功的的广播,会将被安装的app的包移除,防止一些应用利用这个后门在安装完成之后就启动了
        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--;
                        }
                    }
                }
            }
        }

        //按照优先级合静态广播接收者和动态广播接收者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.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 ((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);

        //...
        
        //replaceOrderedBroadcastLocked方法会覆盖已有的广播
        //有序广播和无序广播使用的是不同的队列,因此方法名上面稍微有些区别
        boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
        if (!replaced) {
            //BroadcastRecord入队列
            queue.enqueueOrderedBroadcastLocked(r);
            //开始发送广播
            queue.scheduleBroadcastsLocked();
        }
    }

    return ActivityManager.BROADCAST_SUCCESS;
}
复制代码

代码有点多,总结一下

  1. 做一些权限检查,阻止非系统代码发送受保护的广播
  2. 对某些特定action的广播做一些处理,如针对ACTION_PACKAGE_REMOVED这种广播,需要将包所对应的Activity从历史栈中移除
  3. 保存粘性广播
  4. 解析广播对应的静态/动态广播接收者
  5. 发送无序广播
  6. 按优先级合并静态广播接收者和动态广播接收者,然后发送有序广播

在这过程中有两点需要值得注意

  1. 有序/无序广播在处理动态、静态注册的广播接收者时,在策略上有什么不同?
  2. FLAG_RECEIVER_REPLACE_PENDING标志对于广播会产生什么影响?

先来回答第一个问题 1. 有序/无序广播在处理动态、静态注册的广播接收者时,在策略上有什么不同? 在代码中有序、无序广播通过ordered变量进行标志,动态、静态广播接收者使用了两个列表类型的变量进行保存,分别是registeredReceiversreceivers。 发送有序广播:此时并不会走发送无序广播的流程,而是将动/静态广播接收者按优先级进行合并,然后发送有序广播。这没什么问题 发送无序广播:进入无序广播发送流程,发送完毕之后会将registeredReceivers置空。无序广播发送完毕之后,仍然会进入有序广播发送流程。而此时由于registeredReceiversnull,参与有序广播的接收者全是静态广播接收者。

因此,对于静态广播接收者,无论广播是否有序,广播的都是按照接收者的优先级进行派发的。 而动态广播接收者就会受到广播是否有序的影响。(所以在下面分析processNextBroadcast方法时无序广播的逻辑里面我们是看不到静态接受者的)

2. FLAG_RECEIVER_REPLACE_PENDING标志对于广播(无论是否有序)会产生什么影响? FLAG_RECEIVER_REPLACE_PENDING标志对于广播的影响体现在当前的广播队列中已经存在相等的广播(判定条件为Intent.filterEquals)时。 在上面这个前提下,如果Intent带有FLAG_RECEIVER_REPLACE_PENDING标志,那么只更新已有的广播。也即是该广播只会触发一次。 否则,在更新已有广播之余,还会将当前的广播加入到队列末尾,也即是相同的广播会触发多次。

2. BroadcastQueue.processNextBroadcast方法

ActivityManagerService.broadcastIntentLocked方法最终会调用BroadcastQueue.scheduleBroadcastsLocked方法继续广播发送的工作。 scheduleBroadcastsLocked内部只是发送了一个BROADCAST_INTENT_MSG消息,最终会触发processNextBroadcast方法。

由于该方法代码量较多,我们分成两部分进行讲解:无序广播、有序广播。

2.1 无序广播

final void processNextBroadcast(boolean fromMsg) {
    //...
    //mParallelBroadcasts类型为ArrayList<BroadcastRecord>
    //保存着所有的无序广播
    while (mParallelBroadcasts.size() > 0) {
        r = mParallelBroadcasts.remove(0);
        r.dispatchTime = SystemClock.uptimeMillis();
        r.dispatchClockTime = System.currentTimeMillis();
        final int N = r.receivers.size();
        //...log
        for (int i=0; i<N; i++) {
            Object target = r.receivers.get(i);
            //...log
            deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
        }
        addBroadcastToHistoryLocked(r);
        //...log
    }
    //...
}
复制代码

无序广播代码量较少,结构很简单,就是遍历无序广播列表,然后为每个广播的广播接收者分发消息。 deliverToRegisteredReceiverLocked方法内部会进行权限检查,然后经历下面的调用链 deliverToRegisteredReceiverLocked-> performReceiveLocked-> ApplicationThread.scheduleRegisteredReceiver-> IIntentReceiver.performReceive 最终触发BroadcastReceiver.onReceive方法。 整个过程并不复杂,且与主流程关系并不大,因此不进行深入分析了。

2.2 有序广播

有序广播的代码比较长,有450行左右,里面包含了对静态广播接收者、动态广播接收者、应用进程启动、广播超时处理等流程,会比较复杂一些。

final void processNextBroadcast(boolean fromMsg) {
    //...无序广播
    
    boolean looped = false;
            
    //region 这个do..while循环的作用好像是将有序广播列表中移除一些已经派发完或者。。。的广播
    do {
        //有序广播列表为空,直接返回
        if (mOrderedBroadcasts.size() == 0) {
            //...
            return;
        }
       
        //取出当前有序广播列表中的第一个BroadcastRecord
        r = mOrderedBroadcasts.get(0);
        boolean forceReceive = false;

        //广播超时处理
        int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
        //dispatchTime记录当前广播向第一个接收者开始发消息的时间
        if (mService.mProcessesReady && r.dispatchTime > 0) {
            long now = SystemClock.uptimeMillis();
            if ((numReceivers > 0) &&
                    (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                //...log
                //广播超时处理
                broadcastTimeoutLocked(false); // forcibly finish this broadcast
                forceReceive = true;
                r.state = BroadcastRecord.IDLE;
            }
        }
        
        //BroadcastRecord必须处于IDLE状态才可以继续,否则直接return
        if (r.state != BroadcastRecord.IDLE) {
            //...
            return;
        }

        if (r.receivers == null 
            || r.nextReceiver >= numReceivers//广播已经派发完毕
            || r.resultAbort //上一个广播接收者调用了abortBroadcast终止了广播
            || forceReceive) {//广播超时
            //发送消息给最后一个Receiver,resultTo也即是调用send方法时传入得receiver
            if (r.resultTo != null) {
                try {
                    //...
                    //跨进程调用,最终会触发resultTo.onReceive
                    performReceiveLocked(r.callerApp, r.resultTo,
                        new Intent(r.intent), r.resultCode,
                        r.resultData, r.resultExtras, false, false, r.userId);
                    r.resultTo = null;
                } catch (RemoteException e) {
                    //...
                }
            }
            //endregion

            //取消广播超时消息
            cancelBroadcastTimeoutLocked();

            // ... and on to the next...
            addBroadcastToHistoryLocked(r);
            mOrderedBroadcasts.remove(0);
            r = null;
            looped = true;
            continue;
        }
        //endregion
    } while (r == null);
    //endregion

    //广播接收者存储在一个LIst结构中
    //recIdx是下一个广播接收者的索引
    int recIdx = r.nextReceiver++;

    //记录开始时间,用于处理超时
    r.receiverTime = SystemClock.uptimeMillis();
    if (recIdx == 0) {
        r.dispatchTime = r.receiverTime;
        r.dispatchClockTime = System.currentTimeMillis();
    }

    //发送一个超时消息,处理超时逻辑
    if (!mPendingBroadcastTimeoutMessage) {
        long timeoutTime = r.receiverTime + mTimeoutPeriod;
        //...log
        //该方法会发送一个BROADCAST_TIMEOUT_MSG消息
        setBroadcastTimeoutLocked(timeoutTime);
    }

    final BroadcastOptions brOptions = r.options;
    //拿到广播接收者对象
    //如果是动态广播接收者,此处是一个BroadcastFilter实例
    //如果是静态广播接受这,此处是一个ResolveInfo实例
    final Object nextReceiver = r.receivers.get(recIdx);
    
    
    //...处理动态广播接收者
    //...处理静态广播接收者
}
复制代码

上面这段代码是有序广播在处理静态接收者和动态接收者时的通用逻辑,它主要做了这些事情

  1. 首先进入一个while循环,拿到有序广播列表(mOrderedBroadcasts)中的第一个元素,检查是否超时,如果是,则进行超时处理(进入broadcastTimeoutLocked方法)。其次,还检测了广播是否已经发送给了全部的接收者,如果是,则还需要发送一个广播给最后一个接收者(也即是调用发送广播方法时传入的那个接收者)。如果广播既没有超时,且还未将广播发送给所有的接收者,那么跳出循环进入下一步。总结一句话就是找到下一个广播接收者。
  2. 记录广播发送的开始时间,这里有两个时间,receiverTime用来记录处理下一个接收者的开始时间,dispatchTime用来记录当前的广播在处理第一个接收者时的时间。
  3. 发送一个广播超时消息BROADCAST_TIMEOUT_MSG,用以处理超时。
2.2.1 动态广播接收者
final void processNextBroadcast(boolean fromMsg) {
    //...
    if (nextReceiver instanceof BroadcastFilter) {
        BroadcastFilter filter = (BroadcastFilter)nextReceiver;
        //...log
        //app进程:onReceive方法执行完毕之后会走BroadcastReceiver.finish方法回调AMS最终又进入到processNextBroadcast方法中
        deliverToRegisteredReceiverLocked(r, filter, r.ordered);
        //如果Receiver已经注销,那么直接调用scheduleBroadcastsLocked处理下一个Receiver
        if (r.receiver == null || !r.ordered) {
            //...
            r.state = BroadcastRecord.IDLE;
            scheduleBroadcastsLocked();
        } else {
           //...
        }
        //region
        return;
    }
    //...
}
复制代码

对于动态广播接收者的处理是比较简单的

  1. 调用deliverToRegisteredReceiverLocked方法进行跨进程调用,触发BroadcastReceiver.onReceive方法,在该方法结束之后会执行BroadcastReceiver.finish方法回调AMS,然后再次触发processNextBroadcast触发下一次的消息派发
  2. 如果此时接收者已经注销,那么调用scheduleBroadcastsLocked触发下一次广播
2.2.2 静态广播接收者
final void processNextBroadcast(boolean fromMsg) {
    //...
    ResolveInfo info =
        (ResolveInfo)nextReceiver;
    ComponentName component = new ComponentName(
            info.activityInfo.applicationInfo.packageName,
            info.activityInfo.name);

    //权限检查 appop检查 防火墙...
    boolean skip = false;
    int perm = mService.checkComponentPermission(info.activityInfo.permission,
            r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
            info.activityInfo.exported);
    if (perm != PackageManager.PERMISSION_GRANTED) {
        if (!info.activityInfo.exported) {
            Slog.w(TAG, "Permission Denial: broadcasting "
                    + r.intent.toString()
                    + " from " + r.callerPackage + " (pid=" + r.callingPid
                    + ", uid=" + r.callingUid + ")"
                    + " is not exported from uid " + info.activityInfo.applicationInfo.uid
                    + " due to receiver " + component.flattenToShortString());
        } else {
            Slog.w(TAG, "Permission Denial: broadcasting "
                    + r.intent.toString()
                    + " from " + r.callerPackage + " (pid=" + r.callingPid
                    + ", uid=" + r.callingUid + ")"
                    + " requires " + info.activityInfo.permission
                    + " due to receiver " + component.flattenToShortString());
        }
        skip = true;
    } else if (info.activityInfo.permission != null) {
        final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
        if (opCode != AppOpsManager.OP_NONE
                && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
                        r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
            Slog.w(TAG, "Appop Denial: broadcasting "
                    + r.intent.toString()
                    + " from " + r.callerPackage + " (pid="
                    + r.callingPid + ", uid=" + r.callingUid + ")"
                    + " requires appop " + AppOpsManager.permissionToOp(
                            info.activityInfo.permission)
                    + " due to registered receiver "
                    + component.flattenToShortString());
            skip = true;
        }
    }
    if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
        r.requiredPermissions != null && r.requiredPermissions.length > 0) {
        for (int i = 0; i < r.requiredPermissions.length; i++) {
            String requiredPermission = r.requiredPermissions[i];
            try {
                perm = AppGlobals.getPackageManager().
                        checkPermission(requiredPermission,
                                info.activityInfo.applicationInfo.packageName,
                                UserHandle
                                        .getUserId(info.activityInfo.applicationInfo.uid));
            } catch (RemoteException e) {
                perm = PackageManager.PERMISSION_DENIED;
            }
            if (perm != PackageManager.PERMISSION_GRANTED) {
                Slog.w(TAG, "Permission Denial: receiving "
                        + r.intent + " to "
                        + component.flattenToShortString()
                        + " requires " + requiredPermission
                        + " due to sender " + r.callerPackage
                        + " (uid " + r.callingUid + ")");
                skip = true;
                break;
            }
            int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
            if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
                    && mService.mAppOpsService.noteOperation(appOp,
                    info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
                    != AppOpsManager.MODE_ALLOWED) {
                Slog.w(TAG, "Appop Denial: receiving "
                        + r.intent + " to "
                        + component.flattenToShortString()
                        + " requires appop " + AppOpsManager.permissionToOp(
                        requiredPermission)
                        + " due to sender " + r.callerPackage
                        + " (uid " + r.callingUid + ")");
                skip = true;
                break;
            }
        }
    }
    if (!skip && r.appOp != AppOpsManager.OP_NONE
            && mService.mAppOpsService.noteOperation(r.appOp,
            info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
            != AppOpsManager.MODE_ALLOWED) {
        Slog.w(TAG, "Appop Denial: receiving "
                + r.intent + " to "
                + component.flattenToShortString()
                + " requires appop " + AppOpsManager.opToName(r.appOp)
                + " due to sender " + r.callerPackage
                + " (uid " + r.callingUid + ")");
        skip = true;
    }
    if (!skip) {
        skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
                r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
    }
    
    //查看广播接受者的singleUser属性
    boolean isSingleton = false;
    try {
        isSingleton = mService.isSingleton(info.activityInfo.processName,
                info.activityInfo.applicationInfo,
                info.activityInfo.name, info.activityInfo.flags);
    } catch (SecurityException e) {
        Slog.w(TAG, e.getMessage());
        skip = true;
    }
    //如果广播接收者声明了singleUser属性,那么必须要有INTERACT_ACROSS_USERS权限
    if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
        if (ActivityManager.checkUidPermission(
                android.Manifest.permission.INTERACT_ACROSS_USERS,
                info.activityInfo.applicationInfo.uid)
                        != PackageManager.PERMISSION_GRANTED) {
            Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
                    + " requests FLAG_SINGLE_USER, but app does not hold "
                    + android.Manifest.permission.INTERACT_ACROSS_USERS);
            skip = true;
        }
    }
    
    //检查app是否crash
    if (r.curApp != null && r.curApp.crashing) {
        //...
        skip = true;
    }
    
    //检查应用是否已安装,是否只限安装用户使用
    if (!skip) {
        boolean isAvailable = false;
        try {
            isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
                    info.activityInfo.packageName,
                    UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
        } catch (Exception e) {
            // all such failures mean we skip this receiver
            Slog.w(TAG, "Exception getting recipient info for "
                    + info.activityInfo.packageName, e);
        }
        if (!isAvailable) {
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                    "Skipping delivery to " + info.activityInfo.packageName + " / "
                    + info.activityInfo.applicationInfo.uid
                    + " : package no longer available");
            skip = true;
        }
    }

    //如果是需要跳过,那么将BroadcastRecord值为IDLE状态,然后调用scheduleBroadcastsLocked
    //触发下一次广播
    if (skip) {
        if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                "Skipping delivery of ordered [" + mQueueName + "] "
                + r + " for whatever reason");
        r.receiver = null;
        r.curFilter = null;
        r.state = BroadcastRecord.IDLE;
        scheduleBroadcastsLocked();
        return;
    }

    r.state = BroadcastRecord.APP_RECEIVE;
    String targetProcess = info.activityInfo.processName;
    r.curComponent = component;
    final int receiverUid = info.activityInfo.applicationInfo.uid;
    // If it's a singleton, it needs to be the same app or a special app
    if (r.callingUid != Process.SYSTEM_UID && isSingleton
            && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
        info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
    }
    r.curReceiver = info.activityInfo;

    //...

    
    ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
            info.activityInfo.applicationInfo.uid, false);
    //应用进程已经启动,那么调用processCurBroadcastLocked派发消息
    if (app != null && app.thread != null) {
        try {
            app.addPackage(info.activityInfo.packageName,
                    info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
            processCurBroadcastLocked(r, app);
            return;
        } catch (RemoteException e) {
            //...
        } catch (RuntimeException e) {
            //...
            return;
        }
    }

    //...log
    //应用进程未启动
    //调用AMS.startProcessLocked启动应用进程
    
    if ((r.curApp=mService.startProcessLocked(targetProcess,
            info.activityInfo.applicationInfo, true,
            r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
            "broadcast", r.curComponent,
            (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                    == null) {
        //...log
        //...
        return;
    }
    
    //并保存当前的BroadcastRecord为mPendingBroadcast
    mPendingBroadcast = r;
    mPendingBroadcastRecvIndex = recIdx;
}        
复制代码

对于静态广播接收者,在真正开始广播之前,首先做了大量的权限判断 之后,出现了一个流程上的分支,也即是应用进程是否已经启动 如果应用进程已经启动,那么调用processCurBroadcastLocked方法发送广播,该方法内部会跨进程调用ApplicationThread.scheduleReceiver方法。最终触发ActivityThread.scheduleReceiver方法,该方法内部会通过反射实例化一个广播接收者实例,并最终调用其onReceive方法处理广播。这部分就不详细去讲了,大家可以自行阅读源码。 如果应用进程未启动,那么就需要先启动应用进程,这里使用了我们非常熟悉的ActivityManagerService.startProcessLocked方法。同时会将当前的广播保存至mPendingBroadcast中。 应用进程启动之后会回调ActivityManagerService.attachApplicationLocked->BroadcastQueue.sendPendingBroadcastsLocked,然后继续广播流程,这部分我们在Service启动流程的时候稍微提到了。有兴趣大家可以自行百度了解相关的知识。

sendPendingBroadcastsLocked

public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
    boolean didSomething = false;
    final BroadcastRecord br = mPendingBroadcast;
    if (br != null && br.curApp.pid == app.pid) {
        if (br.curApp != app) {
            //...
            return false;
        }
        try {
            mPendingBroadcast = null;
            processCurBroadcastLocked(br, app);
            didSomething = true;
        } catch (Exception e) {
            //...
            throw new RuntimeException(e.getMessage());
        }
    }
    return didSomething;
}
复制代码

sendPendingBroadcastsLocked内部会拿到之前记录的广播记录mPendingBroadcast,最终也是调用的processCurBroadcastLocked方法进行广播。同时会将mPendingBroadcastnull。 同时为了防止在进程启动过程中发生了有序广播,processNextBroadcast方法中会用以下代码保证有序广播的有序性

final void processNextBroadcast(boolean fromMsg) {
    //处理无序广播
    
    //如果应用进程已经启动并回调AMS
    //执行了sendPendingBroadcastsLocked
    //就不会进到这个if语句,而是继续下面的
    //有序广播处理
    if (mPendingBroadcast != null) {
        //...
        boolean isDead;
        synchronized (mService.mPidsSelfLocked) {
            ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
            isDead = proc == null || proc.crashing;
        }
        if (!isDead) {
            // It's still alive, so keep waiting
            return;
        } else {
            //...
            //如果进程出现异常,或者crash了
            //那么也会继续下面的有序广播
            mPendingBroadcast.state = BroadcastRecord.IDLE;
            mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
            mPendingBroadcast = null;
        }
    }
    
    //处理有序广播
}
复制代码

只有满足下面的任一条件,才会继续有序广播

  1. 应用进程已经启动,并以回调sendPendingBroadcastsLocked方法,派发了有序广播
  2. 应用进程出现异常,或者crash

至此,广播发送流程也已基本分析完毕

3. 总结

广播发送流程中着重分析了ActivityManagerService.broadcastIntentLockedBroadcastQueue.processNextBroadcast方法。 前者的工作主要有三点:

  1. 权限检查以及一些特殊广播的处理
  2. 保存粘性广播
  3. 寻找广播接收者,并封装成BroadcastRecord对象,加入到BroadcastQueue中等待处理。 通过阅读broadcastIntentLocked的源码,还可以发现一些细节:
  4. 粘性广播不允许设置特定的Component以及权限(receiverPermission)
  5. 如果设置了FLAG_RECEIVER_REGISTERED_ONLY标志,那么静态广播接收者将无法接收到广播
  6. 不允许当前用户的粘性广播与USER_ALL这个用户的粘性广播发生冲突
  7. 如果当前用户已经存在相同的粘性广播,那么老的粘性广播的Intent会被替换成新的Intent
  8. 静态广播接收者都是按顺序接收广播的(有序的),动态广播接收者是否有序则依赖于广播是否有序

后者是广播派发的核心方法,其中包括无序广播和有序广播、静态广播接收者和动态广播接收者的处理:

  1. 派发无序广播,无序广播的目标只有动态广播接收者,因此过程非常简单
  2. 处理有序广播的动态广播接收者
  3. 处理有序广播的静态接收者,如果应用进程还未启动这里还会涉及到进程启动的流程 在阅读源码的过程中我还发现了一些细节:
  4. 在启动应用进程时通过mPendingBroadcast来阻断有序广播
  5. 在派发无序广播的时候并不会发送广播超时消息,也就是说无序广播不会发生ANR
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值