Android安全问题 抢先接收广播 - 内因篇之广播接收器注册流程
导读:本文说明系统是如何注册动态广播以及静态广播,这里主要注意其注册的顺序
这篇文章主要是针对我前两篇文章
之前只给出了结果,并没有给出代码分析,现在给出第一步分的分析
大家都知道,广播接收器分为动态注册和静态注册两种
静态接收,就是配置到manifest.xml文件中,PackageManagerService扫描后记录其信息……
动态接收,就是从代码中注册,通过调用下面的方法实现
Intent android.content.Context.registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
(下面的流程图估计画的比较水,将就看一下吧,得补习一下UML了)
首先分析静态注册Receiver的流程
静态receiver的注册是由PackageManagerService开机的时候负责初始化
(PackageManagerService之后简称为PMS)
PMS在开机的时候会对系统一些目录逐个扫描,解析apk文件。静态广播接收器就是在PMS做这件事情的时候顺便处理的。
PMS会解析apk的manifest文件,查找这里注册的receiver,然后加载到内存中
下面看一下PMS是如何工作的
这部分内容没有什么难度,只要有耐心就行,我画了一个很简单流程图,从调用PMS的构造函数开始

注意,这里有几个同名函数,大家需要分清。并不是同一个函数调用了两次
这里只看几处
1.PMS初始化的时候干了些什么
当然,PMS会做很多很多事情,这里我们只看我们关注的,和这篇文章相关的部分
// Collect all system packages.
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
mSystemInstallObserver = new AppDirObserver(
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
mSystemInstallObserver.startWatching();
scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, );
扫描目录的顺序
system/framework
system/app
vendor/app
data/app
drm/app-private
这里以system/app目录为例
2.下一个要关注的地方是
void com.android.server.pm.PackageManagerService.scanDirLI(File dir, int flags, int scanMode, long currentTime)
private void scanDirLI(File dir, int flags, int scanMode, long currentTime {
String[] files = dir.list();
……
int i;
for (i=; i<files.length; i++) {
File file = new File(dir, files[i]);
……
PackageParser.Package pkg = scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
……
}
}
注意
String[] files = dir.list();
以及之后的for循环
3.之后的部分比较无聊,我们直接跳到parseApplication函数部分
else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
}
这部分就是解析manifest中的receiver部分,大家会很奇怪,receiver为什么会变成一个Activity
此Activity非彼Activity,这个Activity是PackageParser的一个内部类,结构也非常简单
public final static class Activity extends Component<ActivityIntentInfo> {
public final ActivityInfo info;
public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
super(args, _info);
info = _info;
info.applicationInfo = args.owner.applicationInfo;
}
public void setPackageName(String packageName) {
super.setPackageName(packageName);
info.packageName = packageName;
}
……
}
原来,最终是添加到了PMS中的成员变量mReceivers中
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers = new ActivityIntentResolver();、
下面我们看看它是如何add的
void com.android.server.pm.PackageManagerService.ActivityIntentResolver.addActivity(Activity a, String type)
void com.android.server.IntentResolver.addFilter(ActivityIntentInfo f)
public void addFilter(F f) {
......
mFilters.add(f);
int numS = register_intent_filter(f, f.schemesIterator(),
mSchemeToFilter, " Scheme: ");
int numT = register_mime_types(f, " Type: ");
//根据我下面红色文字的假设,这里numS和numT应该都为0
if (numS == && numT == ) {
register_intent_filter(f, f.actionsIterator(),
mActionToFilter, " Action: ");
}
if (numT != ) {
register_intent_filter(f, f.actionsIterator(),
mTypedActionToFilter, " TypedAction: ");
}
}
由于开机启动和接收短信并不涉及MIME Type、Scheme等因素。所有我们只考虑Intent中的Action,MIME Type、Scheme等均不考虑
最后看一下register_intent_filter函数,里面没有任何关于排序的代码,只是按顺序add到list中
private final int register_intent_filter(F filter, Iterator<String> i,
HashMap<String, ArrayList<F>> dest, String prefix) {
if (i == null) {
return ;
} int num = ;
while (i.hasNext()) {
String name = i.next();
num++;
if (localLOGV) Slog.v(TAG, prefix + name);
ArrayList<F> array = dest.get(name);
if (array == null) {
//Slog.v(TAG, "Creating new array for " + name);
array = new ArrayList<F>();
dest.put(name, array);
}
array.add(filter);
}
return num;
}
Action保存在mActionToFilter中记录,之后发送广播的时候,查找接收器还要靠mActionToFilter这个成员变量
每个action对应一个List,含有此action的filter将被保存到同一个List中
我们要注意一个事情,那就是mReceivers保存这些receiver的顺序
那就是一直与#2步的顺序保持一致,没有遭到破坏
甚至并没有根据优先级排序,只是一味的add
---------------------------------------------------------------
静态广播接收器的注册分析完了,之后就是系统发出广播,然后如何去分发给他们了
我们下篇文章再来分析
下面看看动态接收器的注册流程
我们也是画个简单的流程图,只看关键代码

最终会调用到AMS中的registerReceiver函数
其中关键部分如下
ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp,
Binder.getCallingPid(),
Binder.getCallingUid(), receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, );
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
}
mReceiverResolver.addFilter(bf);
mReceiverResolver的类型为IntentResolver<BroadcastFilter, BroadcastFilter>
mReceiverResolver.addFilter(bf);
在上面已经简述过了
最终所有动态注册的receiver都保存到AMS的成员变量mReceiverResolver中
/**
* Resolver for broadcast intents to registered receivers.
* Holds BroadcastFilter (subclass of IntentFilter).
*/
final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver = new IntentResolver<BroadcastFilter, BroadcastFilter>() {
@Override
protected boolean allowFilterResult(BroadcastFilter filter, List<BroadcastFilter> dest) {
IBinder target = filter.receiverList.receiver.asBinder();
for (int i=dest.size()-; i>=; i--) {
if (dest.get(i).receiverList.receiver.asBinder() == target) {
return false;
}
}
return true;
} @Override
protected String packageForFilter(BroadcastFilter filter) {
return filter.packageName;
}
};
总结:
静态广播和动态广播如何注册的,我们已经全部分析完了
静态广播是PackageManagerService负责,保存到其成员变量mReceivers中
动态广播是ActivityManagerService负责,保存到其成员变量mReceiverResolver中
注册的顺序:
动态广播与应用中调用的顺序一致
静态广播参考函数
void com.android.server.pm.PackageManagerService.scanDirLI(File dir, int flags, int scanMode, long currentTime)
注意
String[] files = dir.list();
以及之后的for循环
由于开机启动和接收短信并不涉及MIME Type、Scheme等因素。所有我们这里只考虑Intent中的Action,MIME Type、Scheme等均不考虑
保存的顺序参考函数
void com.android.server.IntentResolver.addFilter(ActivityIntentInfo f)
int com.android.server.IntentResolver.register_intent_filter(F filter,
Iterator<String> i, HashMap<String, ArrayList<F>>
dest, String prefix)
下篇文章将讲述发送广播之后,系统如何查找对应的receiver。在这个过程中,系统才开始考虑优先级。
下篇文章顺便看看隐式Intent是如何查找到目标组件的
Android安全问题 抢先接收广播 - 内因篇之广播接收器注册流程的更多相关文章
- Android安全问题 抢先接收广播 - 内因篇之广播发送流程
导读:本文说明系统发送广播的部分流程,如何利用Intent查找到对应接收器.我们依然只关注接收器的排序问题 这篇文章主要是针对我前两篇文章 android安全问题(四) 抢先开机启动 - 结果篇 an ...
- Android安全问题 抢先拦截短信
同上篇文章一样,这里只陈述结果,代码分析稍后给出 导读:本文叙述如何先于某些伪杀毒软件.病毒.常规软件获取到短信 众所周知,android系统在收到短信息的时候会发送广播,但是此广播是有序广播,也就是 ...
- Android安全问题 抢先开机启动
导读:我们以如何抢先开机启动为例,来说明接收无序广播的静态广播接收器的接收顺序 (注意,文本只是陈述结果,所以叫结果篇,之后的文章再给出源码分析) 首先先说一下android中的广播和广播接收器 广播 ...
- android应用无法接收到广播?
本篇文章记录Android应用无法接收到广播的几种case 1. 没有register 广播其实是一种订阅者模式,所以当然需要先register,register的方式有两种 1.1 through ...
- [android] 手机卫士接收打电话广播显示号码归属地
使用广播接收者接收打电话的意图,显示号码归属地 新建一个类OutCallReceiver继承系统的BroadcastReceiver 重写onReceive()方法 调用getResultData() ...
- 【Bugly干货】Android性能优化典范之多线程篇
本文涉及的内容有:多线程并发的性能问题,介绍了 AsyncTask,HandlerThread,IntentService 与 ThreadPool 分别适合的使用场景以及各自的使用注意事项,这是一篇 ...
- Android基础知识巩固:关于PendingIntent和广播
平时使用广播的场合比较多,但细节的东西,看过了也没有总结,以至于某些场合有小问题,还是要把原理和属性搞清楚才能运用自如. 其实也是自己比较懒,先看别人的blog,有个概念再去官网看英文的能好理解一些. ...
- Android笔记(六十)Android总结:四大组件——BroadcastReceiver篇
什么是BroadcastReceiver BroadcastReceiver是Android体系的四大组件之一,本质上是一种全局的监听器,用于监听系统全局的广播消息,正式因为其本质为全局监听,因此可以 ...
- (转)Android Binder设计与实现 – 设计篇
原文地址(貌似已打不开):Android Binder设计与实现 – 设计篇 ------------------------------------------------------------- ...
随机推荐
- 《Web编程入门经典》
在我还不知道网页的基础结构的时候,我找过很多本介绍Web基础的书籍,其中这本<Web编程入门经典>,我认为是最好的. 这本书内容很全面.逻辑很严谨.结构很清晰.语言文字浅显易懂. 看这本书 ...
- C++ 文件读写方案选型
严格来说, 有 3 种风格. UNIX 底层读写库 c 语言 stdio 标准库 iostream 流 一般的工程中, 底层读写库封装程度太低, 需要自己处理缓存和很多通用的异常场景. 不适合. 网络 ...
- ubuntu10.4 server 配置VPN 安装pptp无法连接外网解决(转)
链接:http://www.ppkj.net/2011/04/30/ubuntu10-4-server-%E5%AE%89%E8%A3%85pptp%E6%97%A0%E6%B3%95%E8%BF%9 ...
- 韩顺平细说Servlet视频系列意外收获之用命令行编译带有包的java类解决方案
命令行编译带有包的java类 在命令行编译这一块,基本上都是新手入门时了解一下,然后就直奔IDE而去.这样固然没错,就怕那些--.然后今天在视频中看到了这种方法,觉得可能会用到,所以就记录下来了,以备 ...
- PHP 魔术方法 __clone __toString(五)
__clone() - 当对象克隆的时候自动加载此方法 __toString() - 当对象需要echo打印输出的时候自动加载此方法 __clone() <?php class example{ ...
- PHP中刷新输出缓冲,立即输出数据
<script type="text/javascript"> function show_message(message) { document.getElement ...
- angularjs--$watch、$watchGroup、$watchCollection含义
angularjs的$watch.$watchGroup.$watchCollection使用方式 如果想在controller里面随时监听一个值的变化那就用$watch <p> ...
- 代码分享:php判断数组是否有序
发布:脚本学堂/PHP编程 编辑:JB02 2013-12-17 14:59:02 [大 中 小] 转自:http://www.jbxue.com/article/14723.html如何判断 ...
- 【WPF】Application应用程序启动
wpf应用程序在启动的时候会自动创建Main函数并调用Application实例的run(),从而启动Application进程.Main函数在一个App.g.cs文件中,App.g.cs文件的位置在 ...
- UVA 725
Description Write a program that finds and displays all pairs of 5-digit numbers that between them ...