【Telephony】SIM卡加载流程解析(AOSP)

一行一行代码讲!SIM卡架构太抽象了,我写了这么个又臭又长的5k字文档。
如果你能看完,说明你也对这玩意上头,前途无量。

认真的看完的会发现我只是着重学习架构流程,像STK apk、CatService和IccFileHandler都还没去研究,没办法,时间有限,下班时间就这么多,写了这个长文档就已经花了半个月,以后有时间再补充。
删减了一些涉及闭源的敏感代码,有疑惑可以指出。

名词解释:

DSDS Dual SIM Dual Standby双卡双待
SSSS Single SIM Single Standby单卡单待
UICC Universal Integrated Circuit Card通用集成电路卡,SIM卡
EUICC embedded UICCeSIM,嵌入式SIM卡

一、SIM初始化涉及到的类

开机过程中SIM卡的工作主要包含以下三个系列的类(适用于DSDS,设备及SSSS设备):

1、UICC族

查询SIM卡状态,读取SIM卡信息,并广播SIM卡状态等。包括以下几个重要的初始化相关的类文件:

UiccContorller.java在PhoneFactory中进行初始化,负责UICC系统的初始化工作,监听并查询SIM卡的变化。
UiccSlot.java负责处理设备上的物理卡槽的信息,与是否插卡无关。
UiccCard.java负责区分UICC卡及EUICC卡,根据卡类型创建不同的卡对象。
UiccProfile.java负责UICC卡上运营商的配置。
UiccCardApplication.java负责处理UICC卡上应用的信息。
UiccCarrierPrivilegeRules.java负责读取和保存UICC卡的权限规则。
IccRecords.java负责读取并保存SIM卡文件信息。

2、Subscription族

记录SIM 卡相关信息到数据库,包括以下两个重要部分:

SubscriptionController.java在PhoneFactory中进行初始化,负责保存和管理SubscriptionInfo信息,信息的来源可以是SIM卡也可以是用户的设置。
SubscriptionInfoUpdater.java在PhoneFactory中进行初始化,负责监听SIM卡的变化,并通知SubscriptionController更新SIM卡信息。

3、MultiSimSetting族

开机设置数据卡,包括以下两个重要部分:

MultiSimSettingController.java在PhoneFactory中进行初始化,管理双卡相关的信息。
ProxyController.java在PhoneFactory中进行初始化,负责两个卡槽能力不一致的双卡产品的数据卡设置。

我把这些类的关系画了个图:(箭头是由谁创建了谁的关系)
在这里插入图片描述

tips:
Controller类,一般是小模块初始化的开端,单例对象,对外提供接口。
像slot、card名词结尾的类,一般是实体的抽象类,非单例,可实例化
我观察了UICC族类的规律,这些类都会在构造器初始化参数,并在类内部定义一个update()函数和一个handler处理器

二、SIM卡初始化三大族类详解

UICC族

uicc族类比较多,我们从UiccContorller开始一个一个类解析一下,介绍一下所有类,由点入面,再去看整个流程怎么走、创建了什么服务。

在Phon进程启动过程中,在流程第三步PhoneFactory.makeDefaultPhone()方法中创建了UiccContorller的对象。
在这里插入图片描述

1、UiccContorller.java

在PhoneFactory中创建并进行初始化,负责UICC系统的初始化工作,监听并查询SIM卡的变化
在这里插入图片描述
看注释,这个类负责在系统中保存所有关于通用集成电路卡的数据(UICC)(也称为SIM)。它也被用作API来获取合适的应用程序,以将它们传递给phone和各个serviceTracker。
创建后,UiccController向RIL注册“on”、“unsol_sim_status_changed”通知。当通知到达时,UiccController将调用getIccCardStatus (GET_SIM_STATUS)得到当前SIM卡状态。从UiccContorller.java开始根据GET_SIM_STATUS请求的响应情况,创建了适当的uicc族类的对象树。
UiccController对象是通过调用make()函数创建的,UiccController是一个单例,make()只能调用一次,如果多次调用就会抛出异常。我们从这个方法入手,一行一行解析一下它的make()函数:
1)首先会去配置文件中获取卡槽数量,对比一下默认的参数,为空就使用配置文件中的参数。
如果TelephonyProperties里面定义了卡槽数量时优先被使用,否则我们会去读取配置文件里面的卡槽数量。
在这里插入图片描述
在这个配置文件里面我们可以配置当前项目的卡槽数量。
在这里插入图片描述
2)从RIL中拿到信息,根据卡槽数量创建相应数量的UiccSlots对象(下一步流程)。

设置卡槽数组,用-1填充无效的卡槽位,注册一个处理程序获得SIM卡槽数量状态改变的通知。然后在循环体中遍历RIL对象,注册状态监听器,然后发送Radio事件等。
在这里插入图片描述
在这里插入图片描述
3)创建电话卡状态改变的通知管理器UiccStateChangedLauncher,导入CARD_STRINGS参数,最后配置文件,是否有配置esim卡数量。
在这里插入图片描述
在这里插入图片描述
我们可以看到这个card_string是在sharedPreferences中获取的。
在这里插入图片描述
4)UiccController类中还实现了Handler机制来处理消息(具体什么是Handler的机制需要提前去了解)
在这里插入图片描述
其中有一个消息类型是EVENT_GET_ICC_STATUS_DONE,从字面意思可以看出这个消息是处理“事件获取ICC状态完成”后的操作。
在这里插入图片描述
当收到Radio的SIM卡STATUS_CHANGED(状态改变)事件消息、SIM卡ON(准备完毕)事件消息、SIM卡AVAILABLE(可用)事件消息时,
在下面这个方法中,调用卡槽对象UiccSlot的update方法,判断SIM卡的前后状态,如果为空或者发生改变,创建一个新的UiccCard对象。这是一个重要的注意点,分析SIM卡问题时我们结合radio日志查看代码,可以将这里作为一个结点缩小我们的问题范围。
在这里插入图片描述
仔细看完这个消息处理器,收到哪些消息后做了什么,以后我们遇到相关的业务可以在这里添加,例如,sim卡刷新的操作、Sim卡不可用的Radio事件等等,处理器中都有相关处理。这里不再赘述,可以自行到UiccController中查看。

接下来看看,为每个卡槽创建的UiccSlots是什么,搞明白它创建了卡槽对象是为什么、SIM卡对象(UiccCard)定义的内容又是啥样。

2、UiccSlots.java

在UiccContorller被创建,负责处理设备上的物理卡槽的信息,与是否插卡无关。

在UiccContorller上根据配置文件的参数创建了相应的卡槽对象(UiccSlots)和SIM卡对象(UiccCard),接下来就是了解这两个,先从卡槽对象UiccSlots.java开始。
这个类注释很简单,就是说定义了SIM卡槽对象,作为实体类对象,我们只需要了解他定义了什么信息、对外提供了什么信息。

老办法,先从注释开始,随后看构造器,看定义参数和方法,最后看处理器。
1)注释中,他和UiccContorller都是一个处理器对象的子类,实现了handleMessage方法处理消息。
在这里插入图片描述
2)构造器中,定义了当前卡槽是否是活跃的(可用的)
在这里插入图片描述
3)定义参数和方法,有一些卡状态对象参数和一些更新卡槽的方法等。

其中,在updata()方法内,对比当前卡状态和之前的卡状态(已插卡、未插卡、错误、受限制),再根据卡状态的变化做一些相关操作。
updata()方法是几乎串联起了UICC族类,当卡的新旧状态发生改变时,整套UICC对象链都会因为update()的嵌套调用完成刷新。
当收到卡槽电压的消息时,且当前卡槽中的uiccCard(SIM卡对象)对象为空,创建SIM卡对象(注意点)。
在这里插入图片描述
在这里插入图片描述
4)处理器中,根据卡移除和卡插入的消息做相应的操作,例如弹出移出卡/插入卡的消息窗口。
在这里插入图片描述

3、UiccCard.java

在UiccContorller被创建,负责区分UICC卡及EUICC卡,根据卡类型创建不同的卡对象。
这个类主要的属性是读取运营商的配置,根据运营商约提供的信息来制定SIM卡对象。
这个类没有类注释,但是顾名思义可以看出它是映射SIM卡的对象,参数和卡槽对象差不多,例如卡状态,卡设置更新等。
1)构造方法
从RIL中拿到卡状态、绑定phoneid,调用刷新方法刷新状态。
在这里插入图片描述
2)upDate()方法
如果当前卡状态是已插入,则开始绑定运营商配置文件
在这里插入图片描述
3)定义的方法很多,大多都是针对绑定的配置文件对象(UiccProfile)进行操作,举例几个
根据运营商类型返回指定的APP
在这里插入图片描述
返回运营商制定的规则
在这里插入图片描述

4、UiccProfile.java

在UiccCard被创建,负责UICC卡上运营商的配置,这个类在UiccCard类中表示运营商配置文件。每个描述文件包含了:多个UiccCardApplication、一个UiccCarrierPrivilegeRules、一个CatService。
比较重要,是我们修改配置的地方,接下来了解一下运营商的配置内容
1)类注释
在这里插入图片描述
配置文件与android.telephony.SubscriptionInfo文件相关,但这两个文件的概念是不同的。android.telephony.SubscriptionInfo文件包含所有订阅信息,而Profile文件包含所有用于从UiccCardApplication文件中获取订阅信息的UiccCardApplication对象参数。
2)构造器
将锁对象、卡对象、phoneID绑定。拿到phone对象设置,同步配置文件对象(UiccProfile)和phone中的app标记。
在这里插入图片描述
setCurrentAppType是设置当前网络类型的标记。如果当前的Phone APP是PHONE_TYPE_GSM(2G)就优先使用UiccController.APP_FAM_3GPP。否则设置成UiccController.APP_FAM_3GPP2。
当然,如果UiccCardApplication有定义需要特定的协议,我们最终会按照UiccCardApplication要求来。
在这里插入图片描述
最后同样是调用upDate()复位配置。
3)upDate()方法
a.拿到卡状态,根据传进来的SIM卡状态参数(IccCardStatus)设置Pin、Gsm、Cdma、Ims等订阅信息。(这个IccCardStatus从UiccCtroller赋值的,从RIL中获取)

b.创建好相应数量(从IccCardStatus拿到)的UiccCardApplication对象

c.创建好CatService

d.发送消息给处理器重新导入运营商必要的规则(具体实现在处理器Handler处理器中)
在这里插入图片描述
4)Handler处理器
注释中说明,我们仍然需要处理以下响应消息,即使UiccProfile已经被处理,因为发送请求的人可能还在等待响应。
有运营商配置发送变化、处理运营商规则等事件的处理。事件很多,列出几个关键处理的方法:
onCarrierPrivilegesLoadedMessage();(导入运营商必要的规则)
handleCarrierNameOverride();(SIM运营商名称重写)
handleSimCountryIsoOverride(SIM卡运营商国家重写);

当我们需要修改时,可以在这些方法做客制化,统一架构,导入运营商公共需求
在这里插入图片描述
配置文件类了解的差不多,那他在初始化更新时创建的多个UiccCardApplication、一个UiccCarrierPrivilegeRules、一个CatService是什么?接下来了解一下。

5、UiccCardApplication.java

在UiccProfile被创建,负责处理UICC卡上应用的信息。默认是八个(IccCardStatus.CARD_MAX_APPS)。
UiccCardApplication所担任的任务主要包括创建并向外提供IccFileHandler、IccRecords对象、提供对SIM卡状态的监听等。
1)构造器
a.绑定uiccProfile对象,设置APP状态和类型、绑定Fdn码、Pin码对象
b.根据IccCardApplicationStatus的type参数创建对应的文件处理器和IccRecords对象
c.如果app状态正常,查询FDN号码、Pin码状态
在这里插入图片描述
2)upDate()方法
a.重新绑定参数(在构造器中参数有说明)
b.重新创建文件处理器、IccRecords对象
c.如果注册表状态发送改变,通知指定的注册人
d.如果app状态变为APPSTATE_READY,则重新查询FDN状态,因为它可能在之前的尝试中失败了。
e.将发生改变的消息发给指定的注册人
在这里插入图片描述
在这里插入图片描述
3)Handler处理器
a.当pin码puk码准备完毕或者发生修改,响应消息回主线程
b.如果收到FND查询完成的消息,设置FDN可用性的参数。收到FDN改变的消息做相应的修改。
c.还有两个事件是添加或移除设备锁
d.最后是控制当前APP可用性的事件
在这里插入图片描述
前面提到在构造器中创建了对应的文件处理器,这个方法是四种可选。先把SIM卡流程大致走完再来深入了解这些细节
在这里插入图片描述

6、UiccCarrierPrivilegeRules.java

在UiccProfile被创建,负责读取和保存UICC卡的权限规则。
1)类注释
从UICC读取并存储运营商权限规则,当这个类被创建时,规则被读取,因此它只应该被创建在UICC可以读取之后(UiccProfile准备完毕后创建。

当UICC被更改时,它应该被删除。

最后aosp还给我们提供了文档。(但是好像访问不了了…)
在这里插入图片描述
2)构造器
这个类的构造器只负责绑定资源:
绑定uiccProfile对象,创建整形的AtomicInteger保证数据的线程安全。
绑定回调函数创建规则数组
打开逻辑通道。
在这里插入图片描述
3)Handler处理器
大部分逻辑处理在处理器中工作
当收到构建逻辑管道的通知时:
a.确认管道是否可用,并记录访问管道的次数
b.使用ARA_M打开逻辑通道,并通知处理器开始传输。
c.如果不能同时从ARA_D和ARA_M程序读取规则,退回到基于pkcs15的ARF。
在这里插入图片描述
当收到传输逻辑管道的通知时:
a.从消息对象中拿到规则,检查是否已经从UICC读取了所有规则字节。对于长负载,我们需要在开始解析之前反复获取它,确保全部拿到。
b.解析规则,然后将规则放入定义好的规则列表。
在这里插入图片描述
在这里插入图片描述
TLV这个类是UiccCarrierPrivilegeRules的内部类,用来解析UICC的数据
在这里插入图片描述
其他的定义的一些方法就是对外提供规则内容、状态的方法。

7、IccRecords.java

在UiccCardApplication构造器中创建,负责读取并保存SIM卡文件信息。同时被创建的还有各种IccFileHandler(文件处理器)

FileHandler是以SIM文件系统为操作对象,而IccRecords是以SIM存储内容为操作对象(IccFileHandler偏重底层实现,IccRecords偏重上层应用)。
IccRecords是一个抽象类,实现类有很多,在使用时,根据app类型创建对应的IccRecords。
在这里插入图片描述
挑选SIMRecords来分析,原本是直接创建的,后面交给工厂来创建了
1)构造器
这个类定义了非常多的参数,没有类注释,,,
a.创建应用交付网络记录对象、语音信箱对象
b.使用RIL设置Sim卡短信
c.重置Records
在这里插入图片描述
2)Records复位(resetRecords()方法)
a.Imsi(国际移动用户识别码)、Msisdn(手机号)、VoiceMail语音邮箱号码、mnc长度、卡id、CarrierNameDisplayCondition(运营商名称显示条件位掩码)、Spdi(显示SPN的PLMN列表)、Pnn网络名称、Gid(“组ID码”,主要用于同一个运营商发行的sim卡,需要区别用于不同的业务时,而采用这个id进行识别与区分)、各种PLMN(公共陆地移动网络)等参数全部重置、最后移除通讯录保存的信息。
在这里插入图片描述
3)Handler处理器
这个处理器干了非常多的事情,简单举例几个:
处理获取IMSI的事件、获取MBI事件、重置语音邮件号码和语音邮件标签、获取电话号码事件、设置电话号码事件、获取ICCID事件、获取SPN事件、获取所有短信事件、获取保存在SIM卡上的短信事件、获取GID事件、获取PLMN相关信息。

非常多,我这个举例的不完整,当我们需要使用到这些信息的时候就可以在这里做客制化。
在这里插入图片描述

Subscription族

记录SIM 卡相关信息到数据库

1、SubscriptionController.java

PhoneFactory在初始化UiccController后马上旧创建了SubscriptionController.java,负责保存和管理SubscriptionInfo信息,信息的来源可以是SIM卡也可以是用户的设置。
数据库表在TelephonyProvider.java已经建好了,SubscriptionController就是将信息插入数据库
在这里插入图片描述
1)继承于ISub,是一个binder
在这里插入图片描述
2)定义的参数
a.定义了一个缓存列表,用来保存活动信息,同样定义了判断是否显示操作员名称的参数
在这里插入图片描述
b.与UiccController一样,都是单例模式
在这里插入图片描述
3)构造器
a.internalInit()方法,初始化数据,如绑定UuccController、清除卡槽的索引,并将SIM卡数据放入缓存,防止连续API调用时重复访问I/O
b.migrateImsSettings()方法,将Ims设置从全局设置迁移到订阅数据库。
在这里插入图片描述
4)添加订阅方法
在这里插入图片描述
将SIM订阅消息插入数据库,处理逻辑太长,我就简单描述一下:
1)第一步,订阅卡信息(插入数据库)

isSubscriptionForRemoteSim()判断是否远程订阅SIM?

如果是,根据SIM_SLOT_INDEX、NAME_SOURCE、ICC_ID、CARD_ID参数联合唯一索引,查询数据库是否已经有记录,如果没有就将订阅记录插入数据库。

如果不是,开始处理本地SIM设备,同样的方式判定数据库是否有记录,没有就插入数据库,但是如果已经有了就需要多处理一步更新操纵。

2)将订阅对应的信息添加进订阅链表

订阅记录有哪些东西?有卡颜色、卡槽索引、所属运营商名称、卡id、订阅编号,可能有特定的显示名称

在这里插入图片描述
在手机pull出来的数据库上看一看手机上的SIM卡信息,订阅的信息都已经放进去了
在这里插入图片描述
这块是SubscriptionController的核心部分,其它的代码都是获取设置以上数据库中的信息的方法,对外提供操作接口。

2、SubscriptionInfoUpdater.java

SubscriptionInfoUpdater是一个处理器负责监听SIM卡的变化,并通知SubscriptionController更新SIM卡信息。
在这里插入图片描述
1)构造器
同样是绑定初始参数
在这里插入图片描述
2)upDate()方法
当系统启动时、运营商规则改变时、换SIM卡时被调用,做一些信息同步
在这里插入图片描述
3)Handler处理器
主要是处理SIM卡导入、SIM拔出、SIM卡未准备、SIM卡I/O错误、SIM卡文件改变、SIM卡无法识别、卡槽变化、刷新注册表等事件。
在这里插入图片描述

MultiSimSetting族

MultiSimSetting族简单介绍下MultiSimSettingController就好了,项目里多卡功能一般客制化也是ui显示的客制,不会对流程上有大的改动

1、MultiSimSettingController

PhoneFactory中初始化完成SubscriptionController之后开始创建MultiSimSettingController,管理双卡相关的信息。
在这里插入图片描述
1)类注释
这个类也是一个处理器类,它确保了在多卡多待的情况下协调不同卡的订阅消息,实现分组订阅。

和SubscriptionController、UiccController一样,MultiSimSettingController也是单例
在这里插入图片描述
2)构造器

初始化参数,绑定SubscriptionController,导入运营商配置的subscribe表、注册运营商配置改变的监听器。
在这里插入图片描述
这个族类主要是对多卡的配置做一个协调,单卡、双卡、三卡都要去根据业务变动,了解了,以后遇到问题再深究。

三、总结

我针对SIM卡初始化架构对每个类做了什么都大致描述,并在这其中可客制化的地方做了一些标注,如果企业需要做统一的SIM公共需求,可以在客制化点上切入,归类xml资源文件,做统一的需求导入。
学习架构是为了好统一导入定制的,不是随便往上面拉shi的!默认流程修改需谨慎,原型项目慎改!

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值