SubscriptionController

前置文章

  1. Android系统之System Server大纲
  2. Android无线电信息管理开篇准备工作
  3. 初识com.android.phone
  4. PhoneInterfaceManager
  5. TelephonyTesgistry
  6. UICC

前言

如果读者对 Android 的 Telephony 不是十分了解,对本文存在很多不解之处的,可以先阅读章节前置文章中的文章,尤其是 《Android无线电信息管理开篇准备工作》、《初识com.android.phone》、《UICC》,以便更好的读懂本文。

SubscriptionController 即 SIM 卡信息控制器,用 Subscription 的描述方式来自 3GPP 协议,SIM 卡可被描述为 Subscriber。SubscriptionController 作为控制器,所以它所担负的责任是 SIM 卡信息的中转和管理工作。

在文章《UICC》中,对 SIM 卡信息的来源于管理有了充分的认识,对理解 SubscriptionController 可以发挥非常重要的作用。在手机开机或 SIM 卡变更等行为时,Phone 中的 RIL 加载到 SIM 卡的信息后,便把数据传至 SubscriptionController。另外需要注意的是,SubscriptionController 没有改变 SIM 卡原始数据(卡文件数据)的能力。

SIM 卡数据管理架构


SIM 卡数据管理架构

本文中提到的 SIM 卡数据,如果没有特别说明,特指 Subscriber 方面的数据,不包括其他用户数据等。

增加SIM卡数据

当有一张新的 SIM 卡插入到手机,手机检测到有 SIM 卡插入,就会发起 SIM 卡数据的查询,UiccController 将查询得到的数据,传递到 SubscriptionController。流程如下:


增加SIM卡数据

下面着重分析一下几个重要的过程

updateSubscriptionInfoByIccId()

synchronized protected /*private*/ void updateSubscriptionInfoByIccId() {
    .....
    for (int i = 0; i < PROJECT_SIM_NUM; i++) {
        if (mInsertSimState[i] == SIM_NOT_INSERT) {
             logd("updateSubscriptionInfoByIccId: No SIM inserted in slot " + i + " this time");
        } else {
             if (mInsertSimState[i] > 0) {
                  .....
             } else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ {
                 //新插入的SIM卡走这个通道,
                 //第一个参数是SIM卡的 ICC id
                 //第二个参数是 Slot id,即卡1、卡2...
                 mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i], i);
             }
        }
    }
    .....
}

这个方法定义在文件 frameworks/opt/telephony/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java 中。

上面的方法中提到了 ICC ID,ICC ID为IC卡的唯一识别号码,共有20位数字组成。手机中通过 ICC ID 作为依据来识别不同的 SIM 卡。

Slot ID 和 手机上的卡槽对应,卡1的 Slot ID 为 0,卡2的 Slot ID 为 2,以此类推。

继续跟踪这个方法的下一步

public int addSubInfoRecord(String iccId, int slotIndex) {
    .....
    try {
        .....
        //SubscriptionController把数据固化在数据库,数据库的操作交由TelephonyProvider完成;
        //数据保存路径为:
        //    1、7.0之前的版本:/data/data/com.android.providers.telephony/databases/telephony.db
        //    2、7.0(含)之后的版本:/data/user_de/0/com.android.providers.telephony/databases/telephony.db
        //数据库表格为 siminfo
        ContentResolver resolver = mContext.getContentResolver();
        //查询数据库中是否已经存在同一张SIM卡的信息,通过 ICC ID 唯一识别SIM卡
        Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
                new String[]{SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
                        SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE},
                SubscriptionManager.ICC_ID + "=?", new String[]{iccId}, null);

        boolean setDisplayName = false;
        try {
            //数据库中如果已经存在同一张SIM的信息,则更新数据库信息;
            //数据库中如果不存在此张SIM卡的信息,则把新的SIM卡的信息插入数据库
            if (cursor == null || !cursor.moveToFirst()) {
                setDisplayName = true;
                //插入新的SIM卡信息到数据库
                Uri uri = insertEmptySubInfoRecord(iccId, slotIndex);
                if (DBG) logdl("[addSubInfoRecord] New record created: " + uri);
            } else {
                .....
                if (value.size() > 0) {
                    resolver.update(SubscriptionManager.CONTENT_URI, value,
                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
                                    "=" + Long.toString(subId), null);
                }
            }
        }.....
        //查询这个卡槽下插过的所有SIM卡数据记录
        cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
                SubscriptionManager.SIM_SLOT_INDEX + "=?",
                new String[] {String.valueOf(slotIndex)}, null);
        try {
            if (cursor != null && cursor.moveToFirst()) {
                do {
                    //subid即subscription id,由数据库中的_id(自增长字段)字段确定;
                    int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
                    //sSlotIndexToSubId中保存当前对应的slot id 的 sub id
                    //这里提一下 phone id,phone id其实和 slot id 是一致的,都是依赖卡槽从零开始编号
                    Integer currentSubId = sSlotIndexToSubId.get(slotIndex);
                    if (currentSubId == null
                            || currentSubId != subId
                            || !SubscriptionManager.isValidSubscriptionId(currentSubId)) {
                        .....
                    }
                } while (cursor.moveToNext());
            }
        }
        .....
    return 0;
}

这个方法定义在文件 frameworks/opt/telephony/src/java/com/android/internal/telephony/SubscriptionController.java 中。

请认真阅读代码中的注释了解这个方法的过程。

如果 SIM 卡数据有变化,系统会发送如下广播:

//android.intent.action.ACTION_SUBINFO_CONTENT_CHANGE
Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE);
mContext.sendBroadcast(intent);
//android.intent.action.ACTION_SUBINFO_RECORD_UPDATED
intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED);
mContext.sendBroadcast(intent);

SIM info数据库主要信息

sim info 字段

SubscriptionController 的介绍就到这里,因为这已经介绍了 SubscriptionController 的核心部分,其它的代码都是获取设置以上数据库中的信息,本文就不一一展开讨论他们了,过程也大多很简单,就是对数据库或者缓存数据进行查询和修改。

总结

SubscriptionController 的代码不多,业务也比较简单,主要弄懂 SubscriptionController 所管理的 SIM 卡信息的来源,传播途径,以及对 SIM 卡信息的维护这三个过程,subscriber 这块的知识在上层的应用基本就贯通了。

微信扫一扫关注更多精彩内容

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 124,399评论 16 534
  • Android5.0开始支持双卡了。另外,对于双卡的卡信息的管理,也有了实现,尽管还不是完全彻底完整,如卡的slo...
    rayxiang阅读 273评论 0 0
  • //gradle 下载慢 //可以直接下载gradle之后放在对应的目录里//或者修改 根目录下的文件bul...
    zeromemcpy阅读 224评论 0 0
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 2,763评论 0 4
  • 今天是周一天气还是比较冷,每到周一都比较紧张,因为周一是升国旗,上学时间提前一点!所以早上要早早起床,帮助两个孩子...
    杜欣阳阅读 37评论 0 0