Android广播剖析之发送广播、接收广播

前言:知其所以,知其所以然。

广播使用的三步骤:

  • 注册广播
  • 发送广播
  • 接收广播

本文主要谈一下《发送广播、接收广播》,广播光注册了还没用,只是告诉大家有这么个广播,但是没有发送,不起作用,因为当前的广播没有被触发,广播接收器也接收不到任何信息。
发送广播的初始触发函数是Context --> sendBroadcast(...),下面有几个重载:

public abstract void sendBroadcast(@RequiresPermission Intent intent);
public abstract void sendBroadcast(@RequiresPermission Intent intent,
            @Nullable String receiverPermission);
public abstract void sendBroadcast(Intent intent,
            @Nullable String receiverPermission,
            @Nullable Bundle options);

//这个开发者一般是不允许调用的
public abstract void sendBroadcast(Intent intent,
            String receiverPermission, int appOp);

我们从最基本的sendBroadcast谈起,谈谈发送广播的流程。


Android广播剖析之发送广播、接收广播_第1张图片
发送广播流程.jpg

上面是发送广播的时序图,我们会根据这张图,一点点的分析发送广播的流程。

1.ContextImpl -> sendBroadcast(Intent intent)

ActivityManager.getService().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                    getUserId());

这儿直接执行binder ipc调用,调用到AMS中,具体的细节在上一章《Android广播剖析之广播注册》已经介绍过。在这里我们介绍一下各个参数的含义:
找到函数定义的地方:

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)
函数参数 含义
caller IApplicationThread binder对象,是ActivityThread中持有的对象,通过这个对象可以在Service端找到调用的进程
intent 传入的意图对象,包含广播的action和一些简单的值
resolvedType Context都会持有一个ApplicationContentResolver对象,这个类继承ContentResolver,如果传入的intent中包含对ContentResolver的处理,可以通过这个获取对应的resolvedType,在Service接收的时候需要这个resolvedType来处理相应的权限问题
resultTo IIntentReceiver binder对象,在上一章注册广播的时候讲过,一般传入的参数是空
resultCode 默认传入Activity.RESULT_OK
resultData 返回的结果数据,传入是空
resultExtras 返回的结果额外数据,传入是空
requiredPermissions 需要的权限,这个针对一些需要特殊权限的广播
appOp 是否需要权限认证
bOptions
serialized
sticky 是否是粘性广播
userId 用户的id

这儿特别提一下:粘性广播这个概念已经在api-21被废弃了,因为google觉得
它们不提供安全性(任何人都可以访问它们),没有保护(任何人都可以修改它们)以及许多其他问题。建议的模式是使用非粘性广播来报告某些内容已更改,并使用另一种机制让应用程序在需要时检索当前值。可见安全性是google越来越重视的。

2.AMS -> 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);

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

这是AMS调用的第一个函数,函数中执行的主要工作是:

  • 认证当前的广播是否合法
  • 根据传入的caller在service获取当前的调用进程
  • 将uid和pid设置好,继续调用下一步函数
2.1 认证当前的广播是否合法
intent = verifyBroadcastLocked(intent);

这里的合法性校验主要做三件事情:

  • 当intent中有文件泄漏风险的话,当前的广播不合法。
  • 如果当前的系统还没有准备好,当前的广播不合法。
  • 如果当前的intent flag中包含Intent.FLAG_RECEIVER_BOOT_UPGRADE,说明当前系统正在升级中,当前的广播不合法。
2.2 获取当前的调用进程
final ProcessRecord callerApp = getRecordForAppLocked(caller);

这个函数中主要从mLruProcesses变量中取相应的ProcessRecord,mLruProcesses使用最近最少使用的算法存储ProcessRecord列表,它构建的地方是attachApplicationLocked(...),每次check到应用的application的时候,就会执行这个方法,就是新启动一个新的进程。执行到这儿,就要更新mLruProcesses中的ProcessRecord,在讲解Activity章节的时候,会详细分析一下这个方法的。

final ArrayList mLruProcesses = new ArrayList();

获取到的ProcessRecord,包含很多进程相关的属性,有兴趣参考一下:

frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
2.3 设置uid与pid
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();

他们直接调用的是Binder中的静态方法:

public static final native int getCallingPid();
public static final native int getCallingUid();

这是两个静态的native方法,getCallingPid() 是当前操作处理的进程号,这个方法是可以跨进程使用的,可以用来检验当前系统服务的身份或者校验权限是否正常。
getCallingPid()返回当前操作处理进程的Linux uid,因为底层使用的是linux kernel,所以使用的native方法也会调用linux kernel uid的那一套逻辑。这儿的uid和pid一样,也是起到校验的作用。

3.AMS->broadcastIntentLocked

这个方法相当重磅,是广播处理的核心方法,一系列重要的逻辑都在这里面处理。我们本小节的目的就是要分解这一个个方法,深入广播处理的逻辑里面,剖析一些整体的处理流程。

3.1 发送广播到一个禁止的user

这种情况下是广播发生是失败的,返回值是:
return ActivityManager.BROADCAST_FAILED_USER_STOPPED

if (userId != UserHandle.USER_ALL && !mUserController.isUserOrItsParentRunning(userId)) {
            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 + " and its parent (if any) are stopped");
                return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
            }
        }

导致失败的情况是:

  • step1:当前的userId不是接收广播的用户,并且当前的用户的parent载体正不在运行中。
  • step2:如果step1不成立,直接跳过,在本步骤不会返回广播失败。
  • step3:如果step1成立,那么继续判断step4
  • step4:如果当前不是SYSTEM_UID或者当前的intent flag不是FLAG_RECEIVER_BOOT_UPGRADE的话,并且当前的intent action不等于Intent.ACTION_SHUTDOWN的话,直接返回BROADCAST_FAILED_USER_STOPPED,说明当前的广播失败因为用户被禁止了。

Note:

  • Intent.FLAG_RECEIVER_BOOT_UPGRADE
    这个广播意图代表boot更新,特殊模型下是可以等系统准备好之后再发送这个广播的。上面步骤中,step1这个大条件就排除了很多情况。
  • Intent.ACTION_SHUTDOWN="android.intent.action.ACTION_SHUTDOWN"
    这个intent action是匹配设备关机的action
3.2 检查一下当前的broadcastOptions

这个BroadcastOptions中有四中只要的变量:

  • mTemporaryAppWhitelistDuration
static final String KEY_TEMPORARY_APP_WHITELIST_DURATION
            = "android:broadcast.temporaryAppWhitelistDuration";
  • mMinManifestReceiverApiLevel
static final String KEY_MIN_MANIFEST_RECEIVER_API_LEVEL
            = "android:broadcast.minManifestReceiverApiLevel";
  • mMaxManifestReceiverApiLevel
static final String KEY_MAX_MANIFEST_RECEIVER_API_LEVEL
            = "android:broadcast.maxManifestReceiverApiLevel";
  • mDontSendToRestrictedApps
    不允许发送广播到限制的应用。
static final String KEY_DONT_SEND_TO_RESTRICTED_APPS =
            "android:broadcast.dontSendToRestrictedApps";

这四个变量分别代表broadcast中重要参数,可以控制当前的广播是否有效等等。它是通过Bundle传递过来的,Bundle中放入这几个参数变量,然后在AMS中转化成BroadcastOptions对象。如果符合broadcastOptions对象中参数设置的要求,当前的广播才合法,否则不合法。

3.3 是否系统调用者的权限检验

这儿的代码用了两个变量:

  • isCallerSystem
    调用者是否来自系统
  • isProtectedBroadcast
    是否是受保护的广播
3.3.1 isCallerSystem赋值
final boolean isCallerSystem;
        switch (UserHandle.getAppId(callingUid)) {
            case ROOT_UID:
            case SYSTEM_UID:
            case PHONE_UID:
            case BLUETOOTH_UID:
            case NFC_UID:
            case SE_UID:
                isCallerSystem = true;
                break;
            default:
                isCallerSystem = (callerApp != null) && callerApp.persistent;
                break;
        }

强调最后一句:(callerApp != null) && callerApp.persistent 当前的调用进程中含有android:persistent = "true"的也是来自系统的,系统app都有这个标识的。

3.3.2 isProtectedBroadcast赋值
final boolean isProtectedBroadcast;
        try {
            isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
        } catch (RemoteException e) {
            Slog.w(TAG, "Remote exception", e);
            return ActivityManager.BROADCAST_SUCCESS;
        }

根据当前的action判断此broadcast是否是受保护的广播。
AppGlobals.getPackageManager().isProtectedBroadcast(action),直接调用到PackageManagerService.java中。

public boolean isProtectedBroadcast(String actionName) {
        // allow instant applications
        synchronized (mProtectedBroadcasts) {
            if (mProtectedBroadcasts.contains(actionName)) {
                return true;
            } else if (actionName != null) {
                // TODO: remove these terrible hacks
                if (actionName.startsWith("android.net.netmon.lingerExpired")
                        || actionName.startsWith("com.android.server.sip.SipWakeupTimer")
                        || actionName.startsWith("com.android.internal.telephony.data-reconnect")
                        || actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) {
                    return true;
                }
            }
        }
  • 当前PMS中维护了一个ArraySet变量,会在解析AndroidManifest.xml的时候将符合条件的action放入集合列表中。
  • 过滤特定的actionName,返回true
3.3.3 通过isCallerSystem与isProtectedBroadcast判断当前的广播是否合法
if (!isCallerSystem) {
            if (isProtectedBroadcast) {
                //step1
            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
                if (callerPackage == null) {
                    //step2
                } else if (intent.getComponent() != null) {
                    if (!intent.getComponent().getPackageName().equals(
                            callerPackage)) {
                        //step3
                    }
                } else {
                    intent.setPackage(callerPackage);
                }
            }
        }

当前判断的大前提是调用者并非来自系统。

  • step1:如果是受保护的广播,直接抛出异常,非系统调用者无法操作受保护的广播
  • step2:如果当前调用者为空, 直接抛出异常。
  • step3:component中的pkgName和调用者中的pkgName对比,双重校验。
3.4 action判断

这儿一长串的代码,将近300行,都是对action的判断,下面会分解这部分的执行步骤,将核心的关键点拎出来。

3.4.1 特殊Intent action处理

这里列出的几个特殊的Intent action:这些intent action的共同点都是只能被系统所使用,普通的应用开发者无权使用。

  • Intent.ACTION_UID_REMOVED
    user Id被从系统中去掉。
  • Intent.ACTION_PACKAGE_REMOVED
    一个存在的应用程序包被从设备中去掉。
  • Intent.ACTION_PACKAGE_CHANGED
    一个存在的应用程序包被改变。
  • Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE
    外部的应用程序无法访问。
  • Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
    外部的应用程序可以访问。
  • Intent.ACTION_PACKAGES_SUSPENDED
    应用程序被挂起。
  • Intent.ACTION_PACKAGES_UNSUSPENDED
    应用程序被取消挂起。

在这些特殊的intent action条件下,需要判断一下当前的uid和pid是否有android.Manifest.permission.BROADCAST_PACKAGE_REMOVED权限,如果没有这个权限,直接抛出异常,因为没有这个权限无法操作这些intent action
下面重点谈一谈其中4个重要的action:

  • Intent.ACTION_PACKAGE_REMOVED 与 Intent.ACTION_PACKAGE_CHANGED
    当前intent中包含的getData()如果不存在则不作其他的操作,如果存在,则取出其中的特殊scheme的值,这个值是设置此action的对接字符串。当前如果是removed的话,需要清楚最近任务、电池服务、权限管理服务的中关闭这个进程的相关数据。
    如果不是removed,是changed,也要杀掉当前的进程,并且清楚原有进程的进程数据,给新进程分配相应值。
  • Intent.ACTION_PACKAGES_SUSPENDED 与 Intent.ACTION_PACKAGES_UNSUSPENDED
    进程的状态发生suspend与unsuspend变化,这会影响最近任务的任务栈,需要更新一下最近任务的服务。
3.4.2 其他intent action处理

这里列出其他的intent action,不作详细的描述了,大家了解即可,都是一些和系统参数相关的action,接收到这种action之后,需要操作一些相关的系统服务。

intent action 描述
Intent.ACTION_PACKAGE_REPLACED 新版本的程序包替换旧版本
Intent.ACTION_PACKAGE_ADDED 应用程序安装的action
Intent.ACTION_PACKAGE_DATA_CLEARED 程序数据被清空
Intent.ACTION_TIMEZONE_CHANGED 时区改变的广播
Intent.ACTION_TIME_CHANGED 时间改变的广播
Intent.ACTION_CLEAR_DNS_CACHE DNS缓存清空的广播
Proxy.PROXY_CHANGE_ACTION 代理改变的广播
android.hardware.Camera.ACTION_NEW_PICTURE 拍照广播
android.hardware.Camera.ACTION_NEW_VIDEO 拍视频广播
3.5 粘性广播的处理

粘性广播已经在新版本上被废弃了,主要这种广播会产生很多安全性和权限的问题,这也可以看出来Android的演变方式,越来越注重隐私和权限了。

3.6 处理广播

上面只是在处理广播之前,我们要确保当前的额广播是符合权限条件的,是合法的(uid与pid的校验合法)等等,等这些验证条件满足之后我们会处理之前注册的广播了。
我们都知道广播有两种形式:

  • 静态注册广播
  • 动态注册广播

那么这两种广播类型,谁的优先级比较高了,代码中已经揭秘了,动态注册的广播优先级比较高。我们按照执行的顺序往下捋。

List receivers = null;
List registeredReceivers = null;

上面两个重要的变量,其中receivers是处理静态注册的广播的receivers的结构。registeredReceivers是动态注册广播的receiver集合。

3.6.1 动态注册广播处理

获取动态注册广播的代码:

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 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);
            }
        }
  • 当调用的uid是SHELL_UID的时候,将所有用户注册的广播都取出来。
  • 调用的uid是其他的,直接取广播就可以的。
    动态注册的广播是在代码中注册,注册的广播会存储在AMS中,取广播的的操作在:
registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false /*defaultOnly*/, userId);

将注册的动态广播取出来,根据action和category等匹配属性。

取出来registeredReceivers之后,接下来开始处理工作。

int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        if (!ordered && NR > 0) {
            // If we are not serializing this broadcast, then send the
            // registered receivers separately so they don't wait for the
            // components to be launched.
            if (isCallerSystem) {
                checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                        isProtectedBroadcast, registeredReceivers);
            }
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            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;
        }
  • 如果是来自系统方面的调用,需要check一下当前的广播是否受保护,也要检查一下当前的action是否符合条件。
  • 取一个存在的BroadcastQueue,里面按照队列的方式放入BroadcastRecord
  • 构建一个BroadcastRecord对象,将进程属性和broadcast属性和注册的registeredReceivers传入BroadcastRecord中
  • 如果当前的应用程序没有被替换,就执行两个调用。
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
  • queue.enqueueParallelBroadcastLocked(r)
    当前的BroadcastRecord ,存入mParallelBroadcasts 列表中。
  • queue.scheduleBroadcastsLocked()
    开始调度广播。调度到BroadcastQueue->scheduleBroadcastsLocked()中,发送一个BROADCAST_INTENT_MSG到此类中的handler中。handleMessage中处理的地方是:
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: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
            }
        }

最终调度的方法是processNextBroadcastLocked(......),这个方法是处理广播调度的核心方法,我们需要好好分析一下这个方法的执行动作。
下面用一个流程图表达一下此方法执行的过程。放在《4.processNextBroadcastLocked方法剖析》中讲解这个调用。

3.6.2 静态注册广播处理

获取静态注册广播的代码:

if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }

静态注册的广播是在AndroidManifest.xml中注册的receiver,其中必定涉及到PMS中的解析操作,将AndroidManifest.xml中的广播解析出来。
到PMS中解析AndroidManifest.xml的操作是:

List newReceivers = AppGlobals.getPackageManager()
                        .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();

这儿大家了解即可,在讲解PMS章节的时候,我会详细分析的。

4.processNextBroadcastLocked方法剖析

无论是动态注册的广播还是静态注册的广播,最终处理的地方都会流转到这儿,所以说这里是处理广播的核心函数。讲清楚这个函数的流程,将有助于我们搞清楚广播的处理的核心要点。


Android广播剖析之发送广播、接收广播_第2张图片
处理广播流程.jpg

这里我将广播处理的整体流程分为3个重要的模块:

  • 前置条件处理模块
  • 权限校验处理模块
  • 广播发送和接收的核心处理模块
4.1 前置条件处理模块
  • 更新cpu状态
    将当前处理时间更新到本地的cpu状态上。
  • 处理并行广播
    平时发送的广播一般都是并行的,如果没有调用发生有序广播,都会发生并行广播的这个变量集合中,这儿处理的就是这个并行广播的集合。
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered, int index)

调用这个函数,其中传入的ordered变量为false,代表此时处理的是并行广播。这个函数主要是校验权限,核心的处理是针对有序的。下面将有序的时候再讲一下。
然后执行:

addBroadcastToHistoryLocked(r);

将当前的BroadcastQueue放入历史列表中。接下来 处理的时候会用到。

  • 处理即将要处理的广播
    何为pendingBroadcast,就是针对那些本来即将处理,但是因为receiver所在的进程没有启动,导致当前的broadcast暂时无法处理,所以标记为pendingBroadcast。
  • 处理有序广播
    这里的有序广播就是我们发送的时候使用public void sendOrderedBroadcast(Intent intent, String receiverPermission)或者其重载函数发送的有序广播,因为是有序广播,所以要因此处理,那就会存在超时的情况。
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
            if (mService.mProcessesReady && r.dispatchTime > 0) {
                long now = SystemClock.uptimeMillis();
                if ((numReceivers > 0) &&
                        (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                    broadcastTimeoutLocked(false); // forcibly finish this broadcast
                    forceReceive = true;
                    r.state = BroadcastRecord.IDLE;
                }
            }

这个mTimeoutPeriod是构造的时候传入的参数:

BroadcastQueue(ActivityManagerService service, Handler handler,
            String name, long timeoutPeriod, boolean allowDelayBehindServices) {
        mService = service;
        mHandler = new BroadcastHandler(handler.getLooper());
        mQueueName = name;
        mTimeoutPeriod = timeoutPeriod;
        mDelayBehindServices = allowDelayBehindServices;
    }

当处理broadcast处理时间超过 (2mTimeoutPeriodnumReceivers),此时广播超时,开始进入超时广播的处理流程。执行超时广播的函数是:

 broadcastTimeoutLocked(false);
  • 处理超时广播
    广播超时的时候执行的函数是broadcastTimeoutLocked(boolean fromMsg),其中的参数fromMsg表示当前的函数执行是否是通过Handler->sendMessage来调用的,一个是抛出到另一个线程中执行,一个就在当前线程中执行。这也要分情况讨论。
    当fromMsg为true的时候:
    (1)如果当前系统没有准备好,则直接退出。程序中多次使用mService.mProcessesReady来标识程序是否准备好,这是一个volatile类型变量,在AMS->systemReady(...)中赋值。
    (2)设置广播超时时间再继续处理:执行setBroadcastTimeoutLocked(long timeoutTime),timeoutTime时间之后再处理这个广播。
    下面的执行与fromMsg无关:
    (3)当前的广播已经结束,直接返回
    (4)结束当前广播的receiver,并从receivers中移除。
    (5)发送一个anr发生的消息,告知当前的广播导致anr了。
if (!debugging && anrMessage != null) {
            // Post the ANR to the handler since we do not want to process ANRs while
            // potentially holding our lock.
            mHandler.post(new AppNotResponding(app, anrMessage));
        }
4.2 权限校验处理模块

这儿一长串代码,有几个权限判断的关键变量:

  • ResolveInfo info = (ResolveInfo)nextReceiver;
  • ComponentName component = new ComponentName(info.activityInfo.applicationInfo.packageName,
    info.activityInfo.name);
  • boolean skip = false;
  • int perm = mService.checkComponentPermission(info.activityInfo.permission,
    r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
    info.activityInfo.exported);
    这个判断当前广播权限是否满足的变量。

这儿的nextReceiver是从BroadcastRecord中receivers变量中取出来的,当nextReceiver是BroadcatsFilter对象的时候,前面说过,这时候当前广播的Component为null,说明此时广播没有接收者,是一个空闲广播。反之如果nextReceiver是ResolveInfo对象的时候,说明广播的Component不为null,可以接着往下处理。

==============================================================
上面的skip变量是权限模块贯彻到底的判断变量,会在每个权限判断中赋值,当前权限不通过,skip=true,那就不需要继续向下判断权限了。给大家看一下代码小例子。

if (!skip && r.appOp != AppOpsManager.OP_NONE
                && mService.mAppOpsService.noteOperation(r.appOp,
                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
                != AppOpsManager.MODE_ALLOWED) {
            skip = true;
        }
        if (!skip) {
            skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
                    r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
        }

可以看出来,如果上面skip=true,接下来直接跳出所有的权限判断,直接到最下面一步。

if (skip) {
            r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
            r.receiver = null;
            r.curFilter = null;
            r.state = BroadcastRecord.IDLE;
            r.manifestSkipCount++;
            scheduleBroadcastsLocked();
            return;
        }

直接return出去。可以看出这些权限是有优先级的,优先级高的权限放在上面判断处理,优先级低的权限放在下面处理。

4.3 广播发送和接收的核心处理模块

满足了广播处理的权限判断,接下来开始进入广播分发处理的流程。

r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;

广播状态置为APP_RECEIVE,表明即将接收广播。

4.3.1 设置应用程序不可停止状态

下面的代码执行是向PMS中设置一个值,告诉系统,当前的广播处理已经到了这个状态,前面的前置判断条件已经成功,这时候应用程序不可能停止了,不可抗力的因素除外(电量不足等等)。

try {
            AppGlobals.getPackageManager().setPackageStoppedState(
                    r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
        } catch (RemoteException e) {
        } catch (IllegalArgumentException e) {
            Slog.w(TAG, "Failed trying to unstop package "
                    + r.curComponent.getPackageName() + ": " + e);
        }
4.3.2 当前应用程序在运行

如果当前的应用程序在运行,那么直接处理当前的广播。

processCurBroadcastLocked(r, app, skipOomAdj);

这个处理函数中主要执行的步骤是:

  • 更新ProcessRecord进程状态。
  • 更新AMS中Lru 进程列表。
  • 设置即将调用的intent
  • 跨进程调用到ApplicationThread,从system_server进程到应用程序进程。
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                    mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
                    r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                    app.repProcState);

这个ApplicationThread是一个IApplicationThread binder对象:

private class ApplicationThread extends IApplicationThread.Stub {
    public final void scheduleReceiver(Intent intent, ActivityInfo info,
                CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                boolean sync, int sendingUser, int processState) {
            updateProcessState(processState, false);
            ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                    sync, false, mAppThread.asBinder(), sendingUser);
            r.info = info;
            r.compatInfo = compatInfo;
            sendMessage(H.RECEIVER, r);
        }
}

发送一个H.RECEIVER信息到ActivityThread的handler中处理。最终在ActivityThread->handleReceiver(...)中处理。

private void handleReceiver(ReceiverData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        String component = data.intent.getComponent().getClassName();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);

        IActivityManager mgr = ActivityManager.getService();

        Application app;
        BroadcastReceiver receiver;
        ContextImpl context;
        try {
            app = packageInfo.makeApplication(false, mInstrumentation);
            context = (ContextImpl) app.getBaseContext();
            if (data.info.splitName != null) {
                context = (ContextImpl) context.createContextForSplit(data.info.splitName);
            }
            java.lang.ClassLoader cl = context.getClassLoader();
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            receiver = packageInfo.getAppFactory()
                    .instantiateReceiver(cl, data.info.name, data.intent);
        } catch (Exception e) {
            data.sendFinished(mgr);
        }

        try {
            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);
        } catch (Exception e) {
            data.sendFinished(mgr);
        } finally {
            sCurrentBroadcastIntent.set(null);
        }
        if (receiver.getPendingResult() != null) {
            data.finish();
        }
    }

这是广播最终处理的函数,这个函数主要执行两个步骤:

  • 取出LoadedApk中的receiver,在广播注册的那一篇文章中已经写明这个receiver是如何设置的,请出门左转详细看看。
  • 设置onReceive(...)回调,开发者也会直接在这个继承函数中执行广播的回调。

你可能感兴趣的:(Android广播剖析之发送广播、接收广播)