导读:本文说明系统是如何注册动态广播以及静态广播,这里主要注意其注册的顺序

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

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

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

扫描目录的顺序

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=0; 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应该都为0if (numS == 0 && numT == 0) {register_intent_filter(f, f.actionsIterator(),mActionToFilter, "      Action: ");}if (numT != 0) {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 0;}int num = 0;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, 0);} 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);&nbsp;

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>() {@Overrideprotected boolean allowFilterResult(BroadcastFilter filter, List<BroadcastFilter> dest) {IBinder target = filter.receiverList.receiver.asBinder();for (int i=dest.size()-1; i>=0; i--) {if (dest.get(i).receiverList.receiver.asBinder() == target) {return false;}}return true;}@Overrideprotected 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安全问题 抢先接收广播 - 内因篇之广播接收器注册流程相关推荐

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

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

  2. Android——发送和接收广播

    一,发送广播 利用Intent来发送广播. 使用:在需要发送广播的地方创建一个Intent对象,将信息的内容和用于过滤的信息封装起来,通过以下三种方法将Intent广播出去: 1,Context.se ...

  3. android无法接收短信广播,Android BroadcastReceiver接收收到短信的广播

    一.知识介绍 1.broadcastReceiver是广播接受者,四大组件之一. 2.Android中内置了很多系统级别的广播,可以在应用程序中得到各种系统的状态信息. 3.使用场景: ①当手机没有电 ...

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

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

  5. Android BroadCastReceiver突然失效接收不到广播了

    今天遇到了一个很奇怪的问题,之前做的一个功能通过广播接收器通知更新父fragment 中的ui,一直都是正常的,后来突然发现在三星和小米手机上没有反应了,但华为手机正常,很奇怪,相同的代码,后来看lo ...

  6. 【Android 电量优化】JobScheduler 源码分析 ( JobServiceContext 源码分析 | 闭环操作总结 | 用户提交任务 | 广播接收者接受相关广播触发任务执行 )★

    文章目录 一.JobServiceContext 引入 二.JobServiceContext 源码分析 三.用户在应用层如何使用 JobScheduler 四.用户提交任务 五.广播接收者监听广播触 ...

  7. android实现接收短信,Android 使用 BroadcastReceiver 接收短信

    在 Android 6.0 或者之后的版本中要主动请求用户授权,具体方法,可以参考我之前写的这篇文章 <Android 6.0+ SMSManager 使用示例 发送短信> 广播中接收并处 ...

  8. Android S动态广播注册流程(广播1)

    Android S动态广播注册流程 1. 动态广播注册的流程 2. 新建一个动态广播接收者 3. App部分的registerReceiver 4. system_server侧的广播注册 5. 总结 ...

  9. Android 9.0系统源码_广播(一)广播的注册

    前言 广播作为四大组件之一,使用频率远没有Activity高,但是广播的工作过程还是十分有必要了解的.本系列文章将会逐步讲述广播的注册.发送和接受:而本篇我们要讲的就是广播的注册. 广播的注册分为两种 ...

最新文章

  1. Lambda中的常用sql方法
  2. 查看服务器CPU的个数、CPU的核数、多核超线程数
  3. 湖南工业大学java试卷_湖南工业大学数控加工技术试卷.doc
  4. 怎么关闭或者卸载ivanti_电脑软件卸载不了怎么办,教您解决电脑软件无法卸载方法技巧...
  5. C语言程序设计基础讲座之函数
  6. c语言下列编程段的错误,C语言编程题目(有错误)
  7. 使用XenApp/XenDesktop发布App-V应用——安装App-V服务器
  8. 港科报道 | 8位校友入选香港25青年科创先锋人物
  9. speedoffice(表格)怎么插入文本框?
  10. QQ 微信转链,如何实现淘宝京东苏宁唯品会拼多多,返利转链思路
  11. 那些我接触过的「小而美」的公司
  12. 【线段树区间合并】POJ3667-Hotel
  13. Google Android 原生Rom 下载地址及刷机教程--Factory Images for Nexus and Pixel Devices
  14. nginx的快速入门
  15. 什么是中台(个人理解)
  16. 【复盘】2022年度复盘
  17. [HDRP] PBR Lit shader(一.基础用法篇)
  18. 一个golang编写的redis内存分析工具rma4go
  19. 景观生态学原理| 3 景观格局与分析
  20. ultraiso软碟通注册码

热门文章

  1. jQuery 中 is() 函数常见使用方法
  2. 用strings命令输出文件中的可打印字符
  3. 面部表情自动识别技术及在游戏行业的应用
  4. 感冒了,头晕晕沉沉的,什么时候能好啊!
  5. SQL点滴5—产生时间demention,主要是时间转换
  6. 【F#2.0系列】介绍String类型
  7. Qt中使用的工程文件(.pro文件)
  8. Maven之jar包和项目管理
  9. MQTT初始篇笔记整理
  10. [笔记] consul用grpc做健康检查注意点