SubscriptionInfo是啥

frameworks/base/telephony/java/android/telephony/SubscriptionInfo.java

public class SubscriptionInfo implements Parcelable {
实现Parcelable是为了进程间ipc。
 @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeInt(mId);   //数据库id,递增主键,每一个iccid的卡会占用1个id        dest.writeString(mIccId);  //sim卡的iccid,每张sim卡是唯一的        dest.writeInt(mSimSlotIndex);  //sim卡插入卡槽值,0是卡1,1是卡2,没有插入则是-1        dest.writeCharSequence(mDisplayName); //sim卡名称,用户可以自定义        dest.writeCharSequence(mCarrierName); //运营商名称        dest.writeInt(mNameSource);  //名称来源,是用户设置或者是从sim卡读取(一般就是运营商名称)等        dest.writeInt(mIconTint);   //sim卡图标染色值,tint的概念可以百度google        dest.writeString(mNumber);  //sim卡关联号码        dest.writeInt(mDataRoaming);  //sim卡是否启用数据漫游        dest.writeInt(mMcc);    //mcc,移动国家码,3位数字,中国是460        dest.writeInt(mMnc);    //mnc,移动网络码,2位数字,如00,01等,表示运营商        dest.writeString(mCountryIso); //国家iso代码         mIconBitmap.writeToParcel(dest, flags);  //sim卡图标    }
SubscriptionInfo各个字段见代码中所加的注释。它是和TelephonyProvider数据库中的siminfo对应的。最常用的字段就是mSimSlotIndex和mId,见 subid和slotid,这里的mSimSlotIndex对应slotid,mId对应subid

packages/providers/TelephonyProvider/src/com/android/providers/telephony/TelephonyProvider.java

        private void createSimInfoTable(SQLiteDatabase db) {            if (DBG) log("dbh.createSimInfoTable:+");            db.execSQL("CREATE TABLE " + SIMINFO_TABLE + "("                    + SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"                    + SubscriptionManager.ICC_ID + " TEXT NOT NULL,"                    + SubscriptionManager.SIM_SLOT_INDEX + " INTEGER DEFAULT " + SubscriptionManager.SIM_NOT_INSERTED + ","                    + SubscriptionManager.DISPLAY_NAME + " TEXT,"                    + SubscriptionManager.CARRIER_NAME + " TEXT,"                    + SubscriptionManager.NAME_SOURCE + " INTEGER DEFAULT " + SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE + ","                    + SubscriptionManager.COLOR + " INTEGER DEFAULT " + SubscriptionManager.COLOR_DEFAULT + ","                    + SubscriptionManager.NUMBER + " TEXT,"                    + SubscriptionManager.DISPLAY_NUMBER_FORMAT + " INTEGER NOT NULL DEFAULT " + SubscriptionManager.DISPLAY_NUMBER_DEFAULT + ","                    + SubscriptionManager.DATA_ROAMING + " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT + ","                    + SubscriptionManager.MCC + " INTEGER DEFAULT 0,"                    + SubscriptionManager.MNC + " INTEGER DEFAULT 0,"                    ...                    + SubscriptionManager.CB_OPT_OUT_DIALOG + " INTEGER DEFAULT 1"                    + ");");            if (DBG) log("dbh.createSimInfoTable:-");        }
建表函数createSimInfoTable,常量都是在SubscriptionManager中定义,可以从名字看出和SubscriptionInfo是对应的,当然后面mtk加了不少字段

SubscriptionInfo就是代表了sim卡的相关数据

SubscriptionInfo的更新

frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneFactory.java

    public static void makeDefaultPhone(Context context) {               ...                SubscriptionController.getInstance().updatePhonesAvailability((PhoneProxy[]) sProxyPhones);               Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");               sSubInfoRecordUpdater = new SubscriptionInfoUpdater(context,                       sProxyPhones, sCommandsInterfaces);               SubscriptionController.getInstance().updatePhonesAvailability(sProxyPhones);               ...    }
PhoneFactory的makeDefaultPhone是com.android.phone进程初始化的起点,和subscription相关的有SubscriptionController和SubscriptionInfoUpdater

frameworks/opt/telephony/src/java/com/android/internal/telephony/SubscriptionController.java

    private SubscriptionController(Phone phone) {        ...        if(ServiceManager.getService("isub") == null) {                ServiceManager.addService("isub", this);        }        if (DBG) logdl("[SubscriptionController] init by Phone");    }

SubscriptionController构造函数中添加了isub系统服务,这个服务后续会用到。

frameworks/opt/telephony/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java

public class SubscriptionInfoUpdater extends Handler
继承自Handler,方便消息处理,该类中更新信息的主要方法是updateSubscriptionInfoByIccId

     synchronized private void updateSubscriptionInfoByIccId() {               ...                contentResolver.update(SubscriptionManager.CONTENT_URI, value,                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="                            + Integer.toString(oldSubInfo.get(0).getSubscriptionId()), null);               ...               mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i], i);               ...    }

该函数非常长,这里只选取update和insert的部分代码展示流程,没有插入过卡当新卡,已经插入过的卡更新数据。插入与否是通过数据库中是否有其iccid记录判断的.更新的逻辑是更新slot值,没有插入的卡会设置卡槽值为-1,插入的卡会设置为0或者1。SubscriptionManager.CONTENT_URI对应于之前提到的TelephonyProvider数据库中的siminfo表uri。

frameworks/base/telephony/java/android/telephony/SubscriptionManager.java

   public Uri addSubscriptionInfoRecord(String iccId, int slotId) {        ...        try {            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));            if (iSub != null) {                // FIXME: This returns 1 on success, 0 on error should should we return it?                iSub.addSubInfoRecord(iccId, slotId);            }        } catch (RemoteException ex) {            // ignore it        }        // FIXME: Always returns null?        return null;    }

插入新卡调用了isub系统服务中的addSubInfoRecord方法,isub之前讲过实现类就是SubscriptionController。

    public int addSubInfoRecord(String iccId, int slotId) {        ...        setDisplayName = true;        ContentValues value = new ContentValues();        value.put(SubscriptionManager.ICC_ID, iccId);        // default SIM color differs between slots        value.put(SubscriptionManager.COLOR, color);        value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);        value.put(SubscriptionManager.CARRIER_NAME, "");        Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);        ...}

这个函数也是非常长的,只截取了插入数据的那部分代码

SubscriptionInfo的更新调用链

这小节展示updateSubscriptionInfoByIccId是怎么被调用的

1.UiccController

frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/UiccController.java
 private UiccController(Context c, CommandsInterface []ci) {        ...        mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);        ...    }
构造函数中向ril注册了EVENT_ICC_STATUS_CHANGED消息,监听sim卡状态变化。
  case EVENT_ICC_STATUS_CHANGED:                mCis[index].getIccCardStatus(obtainMessage(                                EVENT_GET_ICC_STATUS_DONE, index));
sim状态有变化的时候会发送EVENT_GET_ICC_STATUS_DONE消息到UiccController的消息处理方法HandlerMessage中,UiccController本身就是继承自Handler,telephony framework中需要使用消息的基本都用了继承Handler这种方式。该消息处理中使用了ril的getIccCardStatus方法,并传递EVENT_GET_ICC_STATUS_DONE消息做为参数,这个也是ril中使用率很高的技术,方法处理完毕ril会上报该消息回来引起回调。
               case EVENT_GET_ICC_STATUS_DONE:                    if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");                    onGetIccCardStatusDone(ar, index);                    break;
EVENT_GET_ICC_STATUS_DONE会调用onGetIccCardStatusDone方法。
private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {    ...   mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));   ...}
 public void registerForIccChanged(Handler h, int what, Object obj) {        synchronized (mLock) {            Registrant r = new Registrant (h, what, obj);            mIccChangedRegistrants.add(r);            //Notify registrant right after registering, so that it will get the latest ICC status,            //otherwise which may not happen until there is an actual change in ICC status.            r.notifyRegistrant();        }    }
mIccChangedRegistrants是会通知所有注册到它这里的handler,发送的消息值是注册时候传递进的what,参数是注册时传递进去的obj。

2.IccCardProxy

frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
  public IccCardProxy(Context context, CommandsInterface ci, int phoneId) {        ...        mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);        ...}
IccCardProxy构造函数中就向UiccController注册了监听卡变化的事件,UiccController的上报流程已经分析过了。
case EVENT_ICC_CHANGED:    ...    updateIccAvailability();    ...
它同样继承自Handler,收到消息后调用updateIccAvailability
private void updateIccAvailability() {        ...registerUiccCardEvents();        ...}
   private void registerUiccCardEvents() {        ...mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);        ...}
updateIccAvailability在必要的时候调用registerUiccCardEvents,然后注册了EVENT_RECORDS_LOADED消息,该消息上报流程比较复杂,见第三小节,这里先看该消息的处理:
case EVENT_RECORDS_LOADED:    ...    onRecordsLoaded()    ...
    private void onRecordsLoaded() {        broadcastInternalIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_LOADED, null);    }
EVENT_RECORDS_LOADED消息处理中最后会发送一个广播:
   private void broadcastInternalIccStateChangedIntent(String value, String reason) {             ...            Intent intent = new Intent(ACTION_INTERNAL_SIM_STATE_CHANGED);            ...            ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);   }

3.SimRecords

这节解析EVENT_RECORDS_LOADED上报流程 frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/SIMRecords.java
public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {   ...   mCi.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);   ...}
构造方法向ril注册EVENT_SIM_REFRESH消息
            case EVENT_SIM_REFRESH:                ...                handleSimRefresh((IccRefreshResponse)ar.result);                ...
消息处理:
private void handleSimRefresh(IccRefreshResponse refreshResponse){    ...    handleFileUpdate(refreshResponse.efId[i]);    ...}
private void handleFileUpdate(int efid) {    ...     fetchSimRecords();    ...}
protected void fetchSimRecords() {    ...    getIccIdRecord();    ...}
protected void getIccIdRecord() {    sendMessage(obtainMessage(EVENT_GET_ICCID));}
依次调用,注意getIccIdRecord方法是基类中的方法。
 case EVENT_GET_ICCID:        ...        isRecordLoadResponse = true;        ...     if (isRecordLoadResponse) {         onRecordLoaded();     }
EVENT_GET_ICCID消息处理中isRecordLoadResponse 会被设置为true,这样最后会调用onRecordLoaded。isRecordLoadResponse 设置为true不止有这一条路径,其它路径也是类似的流程
protected void onRecordLoaded() {    ...    onAllRecordsLoaded();    ...}
protected void onAllRecordsLoaded() {    ...    mRecordsLoadedRegistrants.notifyRegistrants(           new AsyncResult(null, null, null));    ...}
mRecordsLoadedRegistrants会通知所有注册的handler,这样EVENT_RECORDS_LOADED消息就会发送到IccCardProxy的消息处理函数中

4.SubscriptionInfoUpdater

frameworks/opt/telephony/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
回到第二小节末尾,讲到发送了个广播。SubscriptionInfoUpdater构造函数中注册了IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED广播接受器接收这个广播:
public SubscriptionInfoUpdater(Context context, Phone[] phoneProxy, CommandsInterface[] ci) {     ...     IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);     intentFilter.addAction(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);     ...     mContext.registerReceiver(sReceiver, intentFilter);     ...}
这样SubscriptionInfoUpdater收到通知的链条就搭起来了。
    private final BroadcastReceiver sReceiver = new  BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            ...            } else if (action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) {                ...                } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {                    sendMessage(obtainMessage(EVENT_SIM_LOADED, slotId, -1));            ...     }
广播处理中发送EVENT_SIM_LOADED消息,SubscriptionInfoUpdater同样是继承自Handler:
       case EVENT_SIM_LOADED: {                ...                SubscriptionUpdatorThread updatorThread = new SubscriptionUpdatorThread(                        new QueryIccIdUserObj(null, msg.arg1),                        SubscriptionUpdatorThread.SIM_LOADED);                updatorThread.start();
消息处理中用SubscriptionUpdatorThread开线程继续处理:
    public void run() {            switch (mEventId) {                case SIM_ABSENT:                    handleSimAbsent(mUserObj.slotId);                    break;                case SIM_LOADED:                    handleSimLoaded(mUserObj.slotId);                    break;            ...    }
线程run方法中针对插卡或者拔卡不同分支处理,现只看插卡:
 private void handleSimLoaded(int slotId) {        ...        if (isAllIccIdQueryDone() && needUpdate) {        // MTK-END            updateSubscriptionInfoByIccId();        }        ...}
终于走到了终点

双卡相关类的关系

frameworks/base/telephony/java/android/telephony

SubscriptionInfo.aidl和SubscriptionInfo.java,文章开始已经讲述过
SubscriptionManager.java 为三方app层使用,可以获取和设置当前双卡设置(如当前默认拨号卡);可以进行slotid和subId转换等;可以获取当前的卡信息SubscriptionInfo。它的功能基本都是通过binder调用SubscriptionController服务端来实现。
ISub.aidl  SubscriptionManager使用它和SubscriptionController进行沟通:

public class SubscriptionController extends ISub.Stub {
ISubscriptionListener.aidl 有和IOnSubscriptionsChangedListener类似的方法,但是搜索下没见有地方使用这个类。
IOnSubscriptionsChangedListener.aidl 使用它的地方在SubscriptionManager:

  public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "";        if (DBG) {            logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug                    + " listener=" + listener);        }        try {            // We use the TelephonyRegistry as it runs in the system and thus is always            // available. Where as SubscriptionController could crash and not be available            ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(                    "telephony.registry"));            if (tr != null) {                tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);            }        } catch (RemoteException ex) {            // Should not happen        }    }

可以使用它监听卡信息的变化,看到TelephonyRegistry就能想到它的整个流程原理类似PhoneStateListener。

frameworks/opt/telephony

SubscriptionController.java 运行在phone进程中,是双卡相关功能正真实现端,为SubscriptionManager提供服务。
SubscriptionInfoUpdater.java 运行在phone进程中,监听ril上报事件并更新siminfo表。

Siminfo和SubscriptionManager历史

Android5.0之前源码是没有双卡机制的,双卡代码是各个厂商自己写。siminfo是mtk引入的,而SubscriptionManager是高通引入的。google结合了这两者产生了目前这么复杂的双卡代码,原本各自是没有现在这么复杂的:mtk写的siminfo更新没有这么麻烦,而且siminfo是为了显示sim卡相关信息,日常双卡相关代码是按卡槽走,基本不用siminfo表,双卡设置中开启和关闭sim卡的代码和siminfo也没啥大关系;高通的SubscriptionManager没有subid这个概念,也是只有卡槽的概念,主要的功能是为了双卡设置中开启和关闭sim卡。

不过幸好google统一了代码,各个厂商的通信模块多卡相关代码不再会有那么大的差异了,不过目前4G、视频通话、通话录音等等相关google没有统一,所以差异还是非常大呀


更多相关文章

  1. Android(安卓)graphics画图的点击事件处理
  2. android baidupush
  3. [转]Android事件处理
  4. Android(安卓)Framework分析 ---- 1消息处理机制 java层
  5. Android的消息机制,用Android线程间通信的Message机制,Android中Ha
  6. Android的消息机制,用Android线程间通信的Message机制,Android中Ha
  7. Android调用WebService系列之KSoap2对象解析
  8. Android(安卓)测试工具集02
  9. 安卓调用键盘回车键做保存或调用搜索键执行操作

随机推荐

  1. sql将一个表中的数据插入到另一个表中的
  2. sql多表行转列、级联行转列示例代码
  3. sql时间格式化输出、Convert函数应用示例
  4. 积分获取和消费的存储过程学习示例
  5. MSSQL报错:参数数据类型 text 对于 replac
  6. SQL Server页类型汇总+疑问汇总
  7. SQL Server内存遭遇操作系统进程压榨案例
  8. sql带分隔符的截取字符串示例
  9. SQL Server DBA维护常用语句
  10. SQL Server DBA日常检查常用SQL