Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析(1) 联系客服

发布时间 : 星期五 文章Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析(1)更新完毕开始阅读

要稍微整理一下:

在AMS中,BroadcastReceiver的过滤条件由BroadcastFilter表示,如上图所示,该类继承自IntentFilter。

由于一个BroadcastReceiver可以设置多个过滤条件,故AMS使用ReceiverList来记录一个BroadcastReceiver对应的所有BroadcastFilter。

同时,BroadcastFilter中持有对ReceiverList的引用,用于记录自己属于哪个ReceiverList; ReceiverList中也保存着对IIntentReceiver的引用,用于记录自己对应于哪个BroadcastReceiver。

AMS中利用mRegisterdReceivers这个HashMap,来保存广播对应的ReceiverList,其中的键值就是BroadcastReceiver对应的IIntentReceiver。

同时,AMS中的mReceiverResolver用于保存所有动态注册BroadcastReceiver对应的BroadcastFilter。注意此处的IntentResolver是一个模板类,并不是一个Map类型的数据结构。

这部分流程比较简单,大致如下图所示:

三、sendBroadcast流程分析

分析完广播接收方注册BroadcastReceiver的流程,现在我们来看看广播发送方sendBroadcast的流程。

与registerReceiver一样,Context.java中定义了多个sendBroadcast的接口,但是殊途同归,这些接口最终的流程基本一致。

因此,我们以比较常见的接口入手,看看整个代码的逻辑。

public void sendBroadcast(Intent intent) { ............

String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try {

//StrictMode下,对一些Action需要进行检查 intent.prepareToLeaveProcess(this);

//调用AMS中的接口

ActivityManagerNative.getDefault().broadcastIntent(

mMainThread.getApplicationThread(), intent, resolvedType, null,

Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,

getUserId()); } catch(RemoteException e) { .............. } }

ContextImpl中的sendBroadcast函数比较简单,进行一些必要的检查后,直接调用AMS中接口。

我们跟进流程,看看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) { ..............

synchronized(this) {

//检查Broadcast中Intent携带的信息是否有问题

//例如:Intent中不能携带文件描述符(避免安全隐患) //同时检查Intent的Flag

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

broadcastIntent中对Broadcast对应的Intent进行一些检查后,调用broadcastIntentLocked进行实际的处理。

broadcastIntentLocked函数非常长,大概600行左右吧…….我们只能分段看看它的主要思路。

1 broadcastIntentLocked函数Part I

final int broadcastIntentLocked(.....) { intent = new Intent(intent);

// By default broadcasts do not go to stopped apps. // Android系统对所有app的运行状态进行了跟踪

// 当应用第一次安装但未使用过,或从程序管理器被强行关闭后,将处于停止状态

// 在发送广播时,不管是什么广播类型,系统默认增加了FLAG_EXCLUDE_STOPPED_PACKAGES的flag

// 使得对于所有BroadcastReceiver而言,如果其所在进程的处于停止状态,该BroadcastReceiver将无法接收到广播

intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

//大量条件检查,例如:

//有些Action只能由系统来发送;

//有些Action需要发送方申明了对应权限 ...................

//某些Action,例如Package增加或移除、时区或时间改变、清除DNS、代理变化等,需要AMS来处理

//AMS需要根据Action,进行对应的操作 .................. }

这部分的代码较多,主要是针对具体Action的操作,在分析整体流程时,没有必要深究。 当需要研究对应广播引发的操作时,可以再有针对性的研究。

简单地讲,代码主要工作其实只有两个:

1、根据广播对应Intent中的信息,判断发送方是否有发送该广播的权限;

2、针对一些特殊的广播,AMS需要进行一些操作。

2 broadcastIntentLocked函数Part II

..............

// Add to the sticky list if requested. // 处理粘性广播相关的内容 if (sticky) {

//检查是否有发送权限

if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, callingPid, callingUid)

!= PackageManager.PERMISSION_GRANTED) { .................. }

//粘性广播不能指定接收权限

if (requiredPermissions != null && requiredPermissions.length > 0) { ..............

return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION; }

if (intent.getComponent() != null) { //粘性广播不能指定接收方 ............ }

// We use userId directly here, since the \ // as a separate set of sticky broadcasts.

//当粘性广播是针对特定userId时,判断该粘性广播与系统保存的是否冲突 if (userId != UserHandle.USER_ALL) {

//取出发送给所有user的粘性广播

ArrayMap> stickies = mStickyBroadcasts.get( UserHandle.USER_ALL); if (stickies != null) {

ArrayList list = stickies.get(intent.getAction()); if (list != null) {

int N = list.size(); int i;

for (i=0; i

//发送给特定user的粘性广播,与发送给所有user的粘性广播,action一致时,发生冲突

if (intent.filterEquals(list.get(i))) { //抛出异常