android SubscriptionInfo更新流程
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.javapublic 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没有统一,所以差异还是非常大呀
更多相关文章
- Android(安卓)graphics画图的点击事件处理
- android baidupush
- [转]Android事件处理
- Android(安卓)Framework分析 ---- 1消息处理机制 java层
- Android的消息机制,用Android线程间通信的Message机制,Android中Ha
- Android的消息机制,用Android线程间通信的Message机制,Android中Ha
- Android调用WebService系列之KSoap2对象解析
- Android(安卓)测试工具集02
- 安卓调用键盘回车键做保存或调用搜索键执行操作