导读:本文说明系统发送广播的部分流程,如何利用Intent查找到对应接收器。我们依然只关注接收器的排序问题

这篇文章主要是针对我前两篇文章

android安全问题(四) 抢先开机启动 - 结果篇

android安全问题(五) 抢先拦截短信 - 结果篇

现在给出第二步分的分析

下面就来看看发送广播的流程

Context中的sendBroadCast函数的实现是在ContextImpl中,和发送广播相关的有如下六个函数

void android.app.ContextImpl.sendBroadcast(Intent intent)

void android.app.ContextImpl.sendBroadcast(Intent intent, String receiverPermission)

void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission)

void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

void android.app.ContextImpl.sendStickyBroadcast(Intent intent)

void android.app.ContextImpl.sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

可以分为3组:1普通广播;2Ordered广播;3Sticky广播

不论哪种,最后都会由ActivityManagerService处理

private final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle map, String requiredPermission,
boolean ordered, boolean sticky, int callingPid, int callingUid)

以第一种情况为例,流程图大概是这个样子的

ordered和sticky用来区分上面3组广播

下面我们仔细看看这个方法都干了些什么

删减了一些代码

private final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle map, String requiredPermission,
boolean ordered, boolean sticky, int callingPid, int callingUid) {
...//处理特殊intent
// Add to the sticky list if requested.
...//处理sticky广播
// Figure out who all will receive this broadcast.
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
try {
if (intent.getComponent() != null) {
// Broadcast is going to one specific receiver class...
ActivityInfo ai = AppGlobals.getPackageManager().
getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS);
if (ai != null) {
receivers = new ArrayList();
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
receivers.add(ri);
}
} else {
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== ) {
receivers =
AppGlobals.getPackageManager().queryIntentReceivers(
intent, resolvedType, STOCK_PM_FLAGS);
}
registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false);
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
} final boolean replacePending =
(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != ;
...
int NR = registeredReceivers != null ? registeredReceivers.size() : ;
...//如果广播是非ordered,先处理动态注册的接收器
if (!ordered && NR > ) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
BroadcastRecord r = new BroadcastRecord(intent, callerApp,
callerPackage, callingPid, callingUid, requiredPermission,
registeredReceivers, resultTo, resultCode, resultData, map,
ordered, sticky, false);
...
//mParallelBroadcasts只含有动态注册的receiver
boolean replaced = false;
if (replacePending) {
for (int i=mParallelBroadcasts.size()-; i>=; i--) {
if (intent.filterEquals(mParallelBroadcasts.get(i).intent)) {
if (DEBUG_BROADCAST) Slog.v(TAG,
"***** DROPPING PARALLEL: " + intent);
mParallelBroadcasts.set(i, r);
replaced = true;
break;
}
}
}
if (!replaced) {
mParallelBroadcasts.add(r);
scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = ;
} // Merge into one list.
//如果广播是ordered,合并静态、动态接收器
//否则之前处理过动态接收器,这里registeredReceivers为null
int ir = ;
if (receivers != null) {
...
//合并的过程,注意顺序
int NT = receivers != null ? receivers.size() : ;
int it = ;
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);
}
//如果动态接收器优先级高,那么就插到前面
//否则进入else,然后进行下一轮比较,拿下一个静态接收器与之前的动态接收器比较,直到找到自己的位置才插入进列表中
//在这里,调整好接收器的顺序,同等优先级的,显然动态的要在静态的前面
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() > )
|| resultTo != null) {
BroadcastRecord r = new BroadcastRecord(intent, callerApp,
callerPackage, callingPid, callingUid, requiredPermission,
receivers, resultTo, resultCode, resultData, map, ordered,
sticky, false);
...
if (!replaced) {
mOrderedBroadcasts.add(r);
scheduleBroadcastsLocked();
}
} return BROADCAST_SUCCESS;
}

注意上面函数中提到的其中两个成员变量

/**
* List of all active broadcasts that are to be executed immediately
* (without waiting for another broadcast to finish). Currently this only
* contains broadcasts to registered receivers, to avoid spinning up
* a bunch of processes to execute IntentReceiver components.
*/
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<BroadcastRecord>(); /**
* List of all active broadcasts that are to be executed one at a time.
* The object at the top of the list is the currently activity broadcasts;
* those after it are waiting for the top to finish..
*/
final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<BroadcastRecord>();

如果是非ordered广播,那么mParallelBroadcasts将存储所有动态接收器,然后合并的时候,mParallelBroadcasts设置为null,所以不会合并到receivers中

如果是ordered广播,那么mParallelBroadcasts将合并到receivers中

然后,不管是哪种广播,最后都是调用scheduleBroadcastsLocked继续处理

最终到processNextBroadcast函数上

在看processNextBroadcast函数之前,还有个问题需要解决

receivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS);

registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false);

那就是查询出来的List是什么顺序,之后的处理过程上面都已经看到了,但是这里面是什么顺序我们还没有看到

先看看动态注册的Receiver情况

public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
String scheme = intent.getScheme(); ArrayList<R> finalList = new ArrayList<R>();
...
ArrayList<F> firstTypeCut = null;
ArrayList<F> secondTypeCut = null;
ArrayList<F> thirdTypeCut = null;
ArrayList<F> schemeCut = null; //下面是一些匹配的细节,我们先不关注
// If the intent includes a MIME type, then we want to collect all of
// the filters that match that MIME type.
//这里不涉及MIME,所以这里resolvedType=null
if (resolvedType != null) {
...
} // If the intent includes a data URI, then we want to collect all of
// the filters that match its scheme (we will further refine matches
// on the authority and path by directly matching each resulting filter).
if (scheme != null) {
schemeCut = mSchemeToFilter.get(scheme);
if (debug) Slog.v(TAG, "Scheme list: " + schemeCut);
} // If the intent does not specify any data -- either a MIME type or
// a URI -- then we will only be looking for matches against empty
// data.
if (resolvedType == null && scheme == null && intent.getAction() != null) {
firstTypeCut = mActionToFilter.get(intent.getAction());//在addFilter的时候将其add进去,见上一篇博客
if (debug) Slog.v(TAG, "Action list: " + firstTypeCut);
} FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
//finalList就是我们的结果集
if (firstTypeCut != null) {
//buildResolveList循环List,检查是否符合条件,然后复制到finalList中
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, firstTypeCut, finalList);
}
if (secondTypeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, secondTypeCut, finalList);
}
if (thirdTypeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, thirdTypeCut, finalList);
}
if (schemeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, schemeCut, finalList);
}
sortResults(finalList);
...
return finalList;
}

这里我们也看到关于排序代码,具体如下,很简单

@SuppressWarnings("unchecked")
protected void sortResults(List<R> results) {
Collections.sort(results, mResolvePrioritySorter);
} // Sorts a List of IntentFilter objects into descending priority order.
@SuppressWarnings("rawtypes")
private static final Comparator mResolvePrioritySorter = new Comparator() {
public int compare(Object o1, Object o2) {
final int q1 = ((IntentFilter) o1).getPriority();
final int q2 = ((IntentFilter) o2).getPriority();
return (q1 > q2) ? - : ((q1 < q2) ? : );
}
};

上面就是简单的按优先级排序,大的放在前面,否则位置不动

下面看看静态注册的Receiver情况,最终也会和动态注册的Receiver一样,调用同一个函数

public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) {
//ComponentName之后应该均为null,我们不讨论只发给特定组件的情况,因为那样不涉及优先级和顺序的问题
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
List<ResolveInfo> list = new ArrayList<ResolveInfo>();
ActivityInfo ai = getReceiverInfo(comp, flags);
if (ai != null) {
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
} // reader
//ComponentName=null,所以会执行下面代码
synchronized (mPackages) {
String pkgName = intent.getPackage();
//只考虑pkgName=null的情况,同一个package中,哪个receiver先接收到广播暂时不关心
if (pkgName == null) {
return mReceivers.queryIntent(intent, resolvedType, flags);//最终会调用IntentResolver.queryIntent,上面已经分析过
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers);
}
return null;
}
}

现在看来,上面两个查询都是按优先级从高到低排序的,如果优先级相同,顺序则保持不变

之后是调用scheduleBroadcastsLocked来发广播给每一个receiver

至于广播后续如何处理,我们就不再深究了

到这里已经能看到应用中的接收顺序了

总结:

情况分为两种(scheduleBroadcastsLocked),ordered广播和非ordered广播

非ordered广播

先处理动接收器,然后处理静态接收器

ordered广播

同时处理动态接收器和静态接收器

先将动态接收器与静态接收器合并,保持着与优先级相同的顺序,优先级高的在前面,否则顺序不变。静态接收器与动态接收器优先级相同的话,动态接收器在前

Android安全问题 抢先接收广播 - 内因篇之广播发送流程的更多相关文章

  1. Android安全问题 抢先接收广播 - 内因篇之广播接收器注册流程

    导读:本文说明系统是如何注册动态广播以及静态广播,这里主要注意其注册的顺序 这篇文章主要是针对我前两篇文章 android安全问题  抢先开机启动 - 结果篇 android安全问题  抢先拦截短信 ...

  2. Android安全问题 抢先拦截短信

    同上篇文章一样,这里只陈述结果,代码分析稍后给出 导读:本文叙述如何先于某些伪杀毒软件.病毒.常规软件获取到短信 众所周知,android系统在收到短信息的时候会发送广播,但是此广播是有序广播,也就是 ...

  3. Android安全问题 抢先开机启动

    导读:我们以如何抢先开机启动为例,来说明接收无序广播的静态广播接收器的接收顺序 (注意,文本只是陈述结果,所以叫结果篇,之后的文章再给出源码分析) 首先先说一下android中的广播和广播接收器 广播 ...

  4. android应用无法接收到广播?

    本篇文章记录Android应用无法接收到广播的几种case 1. 没有register 广播其实是一种订阅者模式,所以当然需要先register,register的方式有两种 1.1  through ...

  5. [android] 手机卫士接收打电话广播显示号码归属地

    使用广播接收者接收打电话的意图,显示号码归属地 新建一个类OutCallReceiver继承系统的BroadcastReceiver 重写onReceive()方法 调用getResultData() ...

  6. 【Bugly干货】Android性能优化典范之多线程篇

    本文涉及的内容有:多线程并发的性能问题,介绍了 AsyncTask,HandlerThread,IntentService 与 ThreadPool 分别适合的使用场景以及各自的使用注意事项,这是一篇 ...

  7. Android基础知识巩固:关于PendingIntent和广播

    平时使用广播的场合比较多,但细节的东西,看过了也没有总结,以至于某些场合有小问题,还是要把原理和属性搞清楚才能运用自如. 其实也是自己比较懒,先看别人的blog,有个概念再去官网看英文的能好理解一些. ...

  8. Android笔记(六十)Android总结:四大组件——BroadcastReceiver篇

    什么是BroadcastReceiver BroadcastReceiver是Android体系的四大组件之一,本质上是一种全局的监听器,用于监听系统全局的广播消息,正式因为其本质为全局监听,因此可以 ...

  9. (转)Android Binder设计与实现 – 设计篇

    原文地址(貌似已打不开):Android Binder设计与实现 – 设计篇 ------------------------------------------------------------- ...

随机推荐

  1. 在Java中弹出位于其他类的由WindowsBuilder创建的JFrameApplicationWIndow

    我们一般在使用Java弹出窗体的时候,一般是使用Jdialog这个所谓的"对话框类".但是,如果你不是初学者或研究员,而会在使用Java进行swing项目的开发,那么你很可能用到一 ...

  2. asp.net导出Excel

    注意这种方法,导出的excel没有网格线 当在导出Execl或Word的时候,会发生只能在执行 Render() 的过程中调用 RegisterForEventValidation的错误提示.下面的2 ...

  3. Cassandra1.2文档学习(18)—— CQL数据模型(下)

    三.集合列 CQL 3 引入了一下集合类型: •set •list •map 在关系型数据库中,允许用户拥有多个email地址,你可以创建一个email_addresses表与users表存在一个多对 ...

  4. CentOS-6.3安装配置Nginx

    安装说明 系统环境:CentOS-6.3软件:nginx-1.2.6.tar.gz安装方式:源码编译安装 安装位置:/usr/local/nginx 下载地址:http://nginx.org/en/ ...

  5. NoSQL专家王涛访谈:为什么我们还要做一个NoSQL?

    ChinaUnix:各位网友大家好,今天有幸请到王涛先生到CU做客,与大家交流一些工作经验.首先请王涛先介绍一下自己. 王涛:大家好,我是王涛.过去八年里我一直在IBM多伦多实验室从事DB2引擎研发的 ...

  6. Flex读文本文件

    布局: <s:Group id="> <s:Rect width="100%" height="100%"> <s:fi ...

  7. win32进程间通讯--共享内存

    小白一枚,如有不对,请各位大神多多指教! 最近看了看win32进程间通讯.简单写了写利用共享内存实现进程间通讯 使用共享内存实现进程间通讯: 1.在WM_CREATE消息下创建文件映射内核对象 hMa ...

  8. Oracle表连接

    一个普通的语句select * from t1, t2 where t1.id = t2.id and t1.name = 'a'; 这个语句在什么情况下最高效? 表连接分类: 1. 嵌套循环连接(N ...

  9. 1194: [HNOI2006]潘多拉的盒子 - BZOJ

    Description  Input 第一行是一个正整数S,表示宝盒上咒语机的个数,(1≤S≤50).文件以下分为S块,每一块描述一个咒语机,按照咒语机0,咒语机1„„咒语机S-1的顺序描述.每一块的 ...

  10. MySQL Replication 常用架构

    转自: http://www.cnblogs.com/ggjucheng/archive/2012/11/13/2768879.html 前言 MySQLReplicaion本身是一个比较简单的架构, ...