Telephony解析之UICC

本文代码基于Android 9.0

概述

UICC(Universal Integrated Circuit Card),即:通用集成电路卡,也就是大家通常所说的SIM卡

UICC中可以包括多种逻辑模块:

1.用户标识模块(Subscriber Identity Module,SIM)
2.通用用户标识模块(Universal Subscriber Identity Module,USIM)
3.IP多媒体业务标识模块(IP Multi Media Service Identity Module,ISIM)
4.智能IC卡模块(Removable User Identity Module,RUIM(UIM))
5.特殊RUIM(CSIM)

以上模块,实际上是网络升级换代所对应的产物。SIM 用于 GSM 网络系统中,俗称 2G 网络。USIM 也叫升级版 SIM,多用于 3G 网络系统中。ISIM 则是 4G 时代的产物。RUIM、CSIM 是 CDMA 网络系统中的技术,关于更加详细的 UICC 卡技术,请查阅 3GPP 协议 TS 51.011.

UiccController

UiccController类负责保持系统中有关通用集成电路卡(UICC)的所有知识,也称为SIM卡。它还用作API以获取适当的应用程序以将它们传递给电话和服务跟踪器。通过调用make()函数创建UiccController。UiccController是一个单例,make()只能调用一次,如果多次调用则抛出异常。一旦创建,UiccController就会向RIL注册“on”和“unsol_sim_status_changed”通知。当这样的通知到达时,UiccController将调用getIccCardStatus(GET_SIM_STATUS)。根据GET_SIM_STATUS请求的响应,将创建适当的uicc对象树。

下面是UiccController.java中提供的类图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
*                       UiccController
* #
* |
* UiccSlot[]
* #
* |
* UiccCard
* #
* |
* UiccProfile
* # #
* | ------------------
* UiccCardApplication CatService
* # #
* | |
* IccRecords IccFileHandler
* ^ ^ ^ ^ ^ ^ ^ ^
* SIMRecords---- | | | | | | ---SIMFileHandler
* RuimRecords----- | | | | ----RuimFileHandler
* IsimUiccRecords--- | | -----UsimFileHandler
* | ------CsimFileHandler
* ----IsimFileHandler
*
* Legend: # stands for Composition
* ^ stands for Generalization

结合UiccController类代码,总结一下UiccController的主要功能

1.UiccController是整个UICC事务处理的入口,负责对外提供IccRecords、IccFileHandler、UiccCard、UiccSlot、UiccCardApplication等对象,并完成整个UICC系统的初始化工作。

2.UiccController继承自Handler,因此其核心功能就是事件处理,其中需处理的主要事件在下一条总结中

3.注册SIM卡状态的监听,注册卡槽(Slot)状态监听,注册Redio状态监听

4.获取SIM卡中的所有文件

UiccController初始化

UiccController的初始化是在Phone启动过程中被执行的,在Telephony解析之Phone启动流程中我们可以找到UiccController的初始化过程,具体代码调用如下:

PhoneFactory->makeDefaultPhone()

1
2
// Instantiate UiccController so that all other classes can just call getInstance()
sUiccController = UiccController.make(context, sCommandsInterfaces);

UiccController.make()中负责创建UiccController对象,下面分析其构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private UiccController(Context c, CommandsInterface []ci) {
mContext = c;
mCis = ci;
//获取UICC的物理卡槽数量
int numPhysicalSlots = c.getResources().getInteger(
com.android.internal.R.integer.config_num_physical_slots);
// 物理插槽的最小数量应等于或大于Phone数量,如果小于电话数,将电话数作为物理插槽数。
if (numPhysicalSlots < mCis.length) {
numPhysicalSlots = mCis.length;
}

mUiccSlots = new UiccSlot[numPhysicalSlots];
mPhoneIdToSlotId = new int[ci.length];
Arrays.fill(mPhoneIdToSlotId, INVALID_SLOT_ID);

// 通过RadioConfig注册SIM卡槽的状态变化监听,也就是redio的状态监听
mRadioConfig = RadioConfig.getInstance(mContext);
mRadioConfig.registerForSimSlotStatusChanged(this, EVENT_SLOT_STATUS_CHANGED, null);
for (int i = 0; i < mCis.length; i++) {
//通过RIL对象注册ICC卡状态变化监听
mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, i);

// 一旦调制解调器正确通知unsols如果设备未加密或已被解密或支持FBE,即不在CryptKeeper弹跳中,则在无线电状态可用时读取SIM。
// 否则等待无线电开启。 这是SIM卡被锁定以避免CryptKeeper和SIM解锁屏幕重叠的情况所需要的。
if (!StorageManager.inCryptKeeperBounce()) {
// Redio活动状态
mCis[i].registerForAvailable(this, EVENT_RADIO_AVAILABLE, i);
} else {
// Redio开启事件
mCis[i].registerForOn(this, EVENT_RADIO_ON, i);
}
// Redio不可用事件
mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, i);
// 刷新SIM卡事件
mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, i);
}

mLauncher = new UiccStateChangedLauncher(c, this);
}

由以上代码开始,我这里画出了后续整个UICC系统启动的时序图

uicc_controller

在 Android 系统中,SIM 原始数据的加载和接收由 UiccController 来完成,到最后就是从 SIM 卡的各种文件中把数据加载出来,如 ICCID,手机号码,联系人等等,其中 EF_** 是地址描述,标识该数据在 SIM 文件中的地址

UiccController事件处理

UiccController继承自Handler,因此其核心功能就是事件处理,那么接下来看看handleMessage()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Override
public void handleMessage (Message msg) {
synchronized (mLock) {
Integer phoneId = getCiIndex(msg);

AsyncResult ar = (AsyncResult)msg.obj;
switch (msg.what) {
case EVENT_ICC_STATUS_CHANGED:
mCis[phoneId].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE,
phoneId));
break;
case EVENT_RADIO_AVAILABLE:
case EVENT_RADIO_ON:
mCis[phoneId].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE,
phoneId));
// 所有RIL的插槽状态应该相同,因此只需要查看phoneId == 0的卡槽状态
if (phoneId == 0) {
mRadioConfig.getSimSlotsStatus(obtainMessage(EVENT_GET_SLOT_STATUS_DONE,
phoneId));
}
break;
case EVENT_GET_ICC_STATUS_DONE:
onGetIccCardStatusDone(ar, phoneId);
break;
case EVENT_SLOT_STATUS_CHANGED:
case EVENT_GET_SLOT_STATUS_DONE:
onGetSlotStatusDone(ar);
break;
case EVENT_RADIO_UNAVAILABLE:
UiccSlot uiccSlot = getUiccSlotForPhone(phoneId);
if (uiccSlot != null) {
uiccSlot.onRadioStateUnavailable();
}
mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, phoneId, null));
break;
case EVENT_SIM_REFRESH:
onSimRefresh(ar, phoneId);
break;
default:
break;
}
}
}

总结

最后列出UICC系统中重要的类以及其作用:

  UiccController:整个UICC相关信息的控制接口;监控SIM状态变化;

  UiccCard:UICC卡代码中对应的抽象;

  IccCardStatus:维护UICC卡的状态:CardState & PinState;

  UiccCardApplication:UICC具体的一个应用;负责Pin Puk密码设置解锁,数据的读取,存储;

  CatService:负责SIM Toolkit相关;

  IccConstants:SIM File Address;存储不同数据在Sim卡上的字段地址;SIMRecords等基类;

  SIMRecords /RuimRecords:记录SIM卡上的数据;

  IccFileHandler:读取SIM数据以及接收读取的结果;