已同步更新至个人bloghttp://dxjia.cn/2015/08/android-5-default-sms-app/

题外话:博友们有没有好用的写博客客户端推荐啊,cnblogs推荐的windows live writer和word都试过,都不是很好用啊,本地看着还可以,但发布出来排版就不是那么好看了。

正题:

  Android中短信的接收是这样的一个过程:

  底层先将短信报给FW,FW处理过后,会将短信通过intent广播的形式广播出来,而注册了接收短信广播的APP们,就能收到并处理短信。

Default SMS App

  而android在4.2开始,对操作SMS的app进行了限制,增加了default sms app的概念,只有default app才可以操作短信,而且default sms app可以由用户来指定。

  先来看看整个系统初始化时,是如何来初始化default sms app的:

  依然在FW的phone框架初始化里,PhoneFactory. makeDefaultPhone

首先调用一次SmsApplication.getDefaultSmsApplication方法,并且指定第二个参数updateIfNeeded为true,就是如果没有设置过就自动指定一个。

关于getApplication函数我在 《Android 5.0 双模phone初始化分析》 一文中有讲到,其指定default SMS App的规则:

  • 首先尝试从用户指定的默认app,对应的系统setting key为:sms_default_application;
  • 其次看是否有Google的官方 默认sms app;
  • 如果以上两个都没有,那么就从PM中获取所有注册有完整sms有关的broadcast receiver的app,从中找一个优先级最高的,并将其设定为default app。

  default sms app的值保存在setting db中, Settings.Secure.SMS_DEFAULT_APPLICATION

当然,SmsApplication也提供了set方法来让用户可以手动设置他想使用的default sms app

  另外,在PhoneFactory初始化里我们还看到在调用一次getDefaultSmsApplication后,还调用了另外一个方法:

  这个方法会监听应用程序的安装与卸载,并在有应用被安装或者移除的时候,能够及时自动更新default sms app,已保证default sms app是随时都有设定的。

运营商授权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()函数里创建:

UiccCarrierPrivilegeRules

  先看看该类的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。

Android 5.0 Default SMS App以及运营商授权SMS App的更多相关文章

  1. APP安全环节缺失,手游运营商怎样应对APP破解困境

    2013年手游行业的规模与收入均实现了大幅增长,发展势头强劲.然而,在手游快速发展的同一时候,因为监管.审核等方面存在着漏洞,手机游戏软件被破解后注入恶意代码.盗取用户財产.窃取用户设备信息的现象屡见 ...

  2. android 判断是否有sim卡及运营商

    判断是否有sim卡的方法:   int absent = TelephonyManager.SIM_STATE_ABSENT; if (1 == absent) { Log.d(TAG,"请 ...

  3. Android 判断SIM卡属于哪个移动运营商

    第一种方法:获取手机的IMSI码,并判断是中国移动\中国联通\中国电信 TelephonyManager telManager = (TelephonyManager) getSystemServic ...

  4. 【译】Android 6.0 Changes (机翻加轻微人工校对)

    Android 6.0 Changes In this document Runtime Permissions Doze and App Standby Apache HTTP Client Rem ...

  5. sipp模拟电信运营商VoIP终端测试(SIP协议调试)

    三大运营商都有SIP服务器,用来支持语音对讲,多媒体调度等功能,他们的平台可能不是标准的SIP协议会话. 为了应对没完没了的对接各个厂商的平台,这里再整理了一套协议脚本,毕竟全都是没有意义的无用功,标 ...

  6. iOS获取运营商的相关信息

    1.导入:CoreTelephony.framework 2.添加头文件 #import <CoreTelephony/CTTelephonyNetworkInfo.h> #import ...

  7. Android 6.0 7.0 8.0 一个简单的app内更新版本-okgo app版本更新

    登陆时splash初始页调用接口检查app版本.如有更新,使用okGo的文件下载,保存到指定位置,调用Android安装apk. <!-- Android 8.0 (Android O)为了针对 ...

  8. Android 7.0以上版本 系统解决拍照的问题 exposed beyond app through ClipData.Item.getUri()

    解决方案1: android.os.FileUriExposedException: file:///storage/emulated/0/ilive/images/photophoto.jpeg e ...

  9. android 7.0拍照问题file:///storage/emulated/0/photo.jpeg exposed beyond app through ClipData.Item.getUri

    Android7.0调用相机时出现新的错误: android.os.FileUriExposedException: file:///storage/emulated/0/photo.jpeg exp ...

随机推荐

  1. 第二章——建立一个HelloWorld项目,练习使用git的add/commit/push/pull/fetch/clone等基本命令。比较项目的新旧版本的差别-----答题者:徐潇瑞

    1.首先下载安装git,很简单所以就不详细说了,当弹出一个类似的命令窗口的东西,就说明Git安装成功 2.因为Git是分布式版本控制系统,所以需要填写用户名和邮箱作为一个标识 3.接着,注册githu ...

  2. web压力测试工具

    ab apache 自带的web压力测试工具,window和linux下均有. 命令行:./ab -c 100 -n 1000 http://www.baidu.com 说明: -c 表示同时处理10 ...

  3. 使用的组件:ckeditor

    老牌Web文本编辑器,无需多言. 官网地址:http://ckeditor.com/

  4. 让Sqlite脱离VC++ Runtime独立运行

    前段时间在开发OrayTalk(傲瑞通)的聊天记录模块时用到了Sqlite,这是我第一次接触和使用Sqlite,总体感觉还是非常不错的.这里把我使用Sqlite的经验跟大家分享一下. 一.关于Sqli ...

  5. 将doc文件批量转为pdf文件

    需要将不少doc文件转为pdf,WPS带有这种功能,但是鼠标点击次数太多以后整个人都变得很烦躁 用了一下午去搜这方面的工具软件,找到若干.有一些免费,有一些试用的,但总归就找到一个真正能用,虽说生成的 ...

  6. 构建单页Web应用

    摘自前端农民工的博客 让我们先来看几个网站: coding teambition cloud9 注意这几个网站的相同点,那就是在浏览器中,做了原先“应当”在客户端做的事情.它们的界面切换非常流畅,响应 ...

  7. 易出错的C语言题目之一:宏定义与预处理

    1.写出下列代码的运行结果: #include<stdio.h> #include<string.h> #define STRCPY(a,b) strcpy(a##_p,#b) ...

  8. js关于事件

    摘要:事件在Web前端领域有很重要的地位,很多重要的知识点都与事件有关.本文旨在对常用的事件相关知识做一个汇总和记录. 在前端中,有一个很重要的概念就是事件.我对于事件的理解就是使用者对浏览器进行的一 ...

  9. Sql Server 调用DLL

    背景 在处理数据或者分析数据时,我们常常需要加入一定的逻辑,该些处理逻辑有些sql是可以支持,有些逻辑SQL则无能为力,在这种情况下,大多数人都会编写相关的程序来处理成自己想要的数据,但每次处理相同逻 ...

  10. Mongodb副本集

    Replication:副本集 副本集可以将客户端的写操作分散到不同的服务器,可以用于灾难恢复,报告和备份. 副本集需要一个主服务器和一个备服务器,以及一个仲裁服务器.仲裁服务器决定将哪一个服务器作为 ...