已同步更新至我的blog:http://dxjia.cn/2015/08/android-5-default-sms-app/android
题外话:博友们有没有好用的写博客客户端推荐啊,cnblogs推荐的windows live writer和word都试过,都不是很好用啊,本地看着还能够,但发布出来排版就不是那么好看了。数据库
正题:windows
Android中短信的接收是这样的一个过程:缓存
底层先将短信报给FW,FW处理事后,会将短信经过intent广播的形式广播出来,而注册了接收短信广播的APP们,就能收到并处理短信。app
而android在4.2开始,对操做SMS的app进行了限制,增长了default sms app的概念,只有default app才能够操做短信,并且default sms app能够由用户来指定。框架
先来看看整个系统初始化时,是如何来初始化default sms app的:函数
依然在FW的phone框架初始化里,PhoneFactory. makeDefaultPhoneui
首先调用一次SmsApplication.getDefaultSmsApplication方法,而且指定第二个参数updateIfNeeded为true,就是若是没有设置过就自动指定一个。url
关于getApplication函数我在 《Android 5.0 双模phone初始化分析》 一文中有讲到,其指定default SMS App的规则:spa
default sms app的值保存在setting db中, Settings.Secure.SMS_DEFAULT_APPLICATION
固然,SmsApplication也提供了set方法来让用户能够手动设置他想使用的default sms app
另外,在PhoneFactory初始化里咱们还看到在调用一次getDefaultSmsApplication后,还调用了另一个方法:
这个方法会监听应用程序的安装与卸载,并在有应用被安装或者移除的时候,可以及时自动更新default sms app,已保证default sms app是随时都有设定的。
后来的版本,android又增长了运营商受权SMS App的实现,原则是若是全部的sms app里,若是有一个是运营商受权指定的短信处理app,那么它就会有第一优先级,无论default app设定的是谁,都会只使用这个受权app来收发和管理显示短信。
那么这个运营商受权APP是在哪里指定的呢?答案是:是固化在icc卡里的,也就是运营商给你的手机卡(no-uim和no-sim的手机目前是处理不了的),卡在出厂的时候,会在卡里的某个固定单元文件写上受权app的package name以及其签名hash校验值,在卡初始化完成后读取这些值解析后保存,若是手机里有这个package name的app,而且签名hash也一致,那么就说明该App是运营商受权sms app。
完成这些信息初始化的类为 UiccCarrierPrivilegeRules,其内部完成对卡上文件进行读取和解析,保存信息,并提供对外接口。
由于跟卡直接相关,因此UiccCarrierPrivilegeRules在UiccCard被建立后初始化。
在UiccCard.update()函数里建立:
先看看该类的class注释,
在构造函数中开启读取文件的流程,事件驱动。
注释里对该类的功能进行了讲解,并且给出了使用到的icc card文件读取和解析的spec规范文档 GPD_SE_Access_Control_v1.0.20.pdf,惋惜他给的连接无效了,能够在百度文库上找到该spec,地址: GPD_SE_Access_Control_v1.0.20.pdf
具体读取icc文件和解析这里就不分析了,都是依照spec的实现。只说明下几个接口和内部变量:
AccessRule |
内部类,用来保存解析到的rules,内部维护单个rule的package name和签名hash值。 |
List<AccessRule> mAccessRules; |
保存全部的rules在list,看来能够支持多个运营商指定app |
areCarrierPriviligeRulesLoaded() |
是否已经准备好 |
getCarrierPrivilegeStatus() |
验证指定的package name的app是否有运营商受权 |
getCarrierPrivilegeStatusForCurrentTransaction() |
验证当前进程里是否存在有运营商受权的app(多个app能够经过共享id的形式运行在同一个进程里) |
getCarrierPackageNamesForIntent() |
经过从package manager中取出全部符合传入的Intent的app,也就是取出全部能够处理传入的Intent的app,并检查这些app里是否有符合运营商受权的,并返回符合的list |
以一条新短信的接收为例:
在InboundSmsHandler里的processMessagePart()函数中,processMessagePart()函数用来将缓存的短信分段进行组装,若是已经收全,就会将短信广播出去,固然,若是是单段的独立短信该函数也就直接广播了,来看打包Intent广播的部分:
注意上面代码中黄色高亮的部分,首先是新建一个intent,而这个intent的action直接指定为Intents.SMS_FILTER_ACTION?这个是什么鬼,之前没见过啊。。。跳转过去:
注释已经很清晰明了了,这个action只会发送给carrier app,并且carrier app能够经过set result为RESULT_CANCELED来终止这个广播,这样别的app就永远没有机会收到这个广播了。
回到以前的打包intent的代码,其会去UiccCard里经过 getCarrierPackageNamesForIntent()方法来获得能够处理SMS_FILTER_ACTION的符合运营商受权的app name list,若是能取到,那么就将intent的目标package直接设定为那个app,这样这个短信广播就只会发送给这个受权app;
intent.setPackage(carrierPackages.get(0));
而若是没有运营商受权app,那么就会调用setAndDirectIntent (intent, destPort);来设定广播app,这里才轮到default sms app:
短信的desPort都是-1,因此能够只看上面这个if分支,首先先将intent的action修改成 Intents.SMS_DELIVER_ACTION, 这个是android的新短信常规intent action,顶替掉以前的SMS_FILTER_ACTION;而后经过getDefaultSmsApplication获取到default sms app,若是能取到,那么经过intent.setComponent(componentName)设置目标package为这个app,若是没有,那么就setComponent(null),这样就能够广播给全部能够接收SMS_DELIVER_ACTION的app。
另外,提一点另外的细节,打包广播短信的地方:
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_SMS, resultReceiver, UserHandle.OWNER);
第4个参数,传入的resultReceiver,是个内部类SmsBroadcastReceiver对象,用来处理短信广播的结果,对每种intent action广播出去以后的处理结果都有分别处理,如从缓存数据库总删除短信、更新短信内容等。也用来若是发出去的广播没人处理,则使用最低级的SMS_RECEIVED_ACTION从新将广播广播出去等机制。。
FW初始化时,首先尝试设定一个default sms app,同时,在卡槽的icc卡准备好后,开始读取卡上的运营商受权app数据,并保存下来;新短信接收时首先经过接口获取到运营商受权app,若是没有,再经过接口获取到default sms app,若是尚未,就直接广播啦。
运营商受权app的优先级大于default sms app。