Subscription的获取和更新主要依靠SubscriptionInfoUpdater类。在PhoneApp启动的过程中会创建SubscriptionInfoUpdater类的对象,从下面的构造函数里可以看出该类接收了ACTION_SIM_STATE_CHANGED和ACTION_INTERNAL_SIM_STATE_CHANGED两个广播,通过这两个广播中的SIM状态信息,来获取或更新subscription信息。
public SubscriptionInfoUpdater(Context context, Phone[] phone, CommandsInterface[] ci) {
logd("Constructor invoked");
mContext = context;
mPhone = phone;
mSubscriptionManager = SubscriptionManager.from(mContext);
mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
/*创建接收TelephonyIntents.ACTION_SIM_STATE_CHANGED广播的intent filter*/
IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
/*添加接收TelephonyIntents.ACTION_INTERNAL_SIM_STATE_CHANGED广播的intent filter。*/
intentFilter.addAction(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiver(sReceiver, intentFilter);
mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
initializeCarrierApps();
}
下面是sReceiver的实现code:
private final BroadcastReceiver sReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
logd("[Receiver]+");
String action = intent.getAction();
logd("Action: " + action);
...
String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
logd("simStatus: " + simStatus);
if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {//处理ACTION_SIM_STATE_CHANGED广播
rebroadcastIntentsOnUnlock.put(slotIndex, intent);
"/**处理ABSENT状态*/"
if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) {
sendMessage(obtainMessage(EVENT_SIM_ABSENT, slotIndex, -1));
} else if (IccCardConstants.INTENT_VALUE_ICC_UNKNOWN.equals(simStatus)) {//处理UNKNOWN状态
sendMessage(obtainMessage(EVENT_SIM_UNKNOWN, slotIndex, -1));
} else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(simStatus)) {//处理IO_ERROR状态
sendMessage(obtainMessage(EVENT_SIM_IO_ERROR, slotIndex, -1));
} else if (IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED.equals(simStatus)) {//处理RESTRICTED状态
sendMessage(obtainMessage(EVENT_SIM_RESTRICTED, slotIndex, -1));
} else {
logd("Ignoring simStatus: " + simStatus);
}
} else if (action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) {//处理ACTION_INTERNAL_SIM_STATE_CHANGED广播
if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(simStatus)) {//处理ICC_LOCKED状态
String reason = intent.getStringExtra(
IccCardConstants.INTENT_KEY_LOCKED_REASON);
sendMessage(obtainMessage(EVENT_SIM_LOCKED, slotIndex, -1, reason));
} else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {//ICC_LOADED状态
sendMessage(obtainMessage(EVENT_SIM_LOADED, slotIndex, -1));
} else {
logd("Ignoring simStatus: " + simStatus);
}
}
logd("[Receiver]-");
}
};
重点分析下面的handleSimLoaded方法:
private void handleSimLoaded(int slotId) {
logd("handleSimStateLoadedInternal: slotId: " + slotId);
// The SIM should be loaded at this state, but it is possible in cases such as SIM being
// removed or a refresh RESET that the IccRecords could be null. The right behavior is to
// not broadcast the SIM loaded.
IccRecords records = mPhone[slotId].getIccCard().getIccRecords();
if (records == null) { // Possibly a race condition.
logd("onRecieve: IccRecords null");
return;
}
if (records.getIccId() == null) {
logd("onRecieve: IccID null");
return;
}
mIccId[slotId] = records.getIccId();
if (isAllIccIdQueryDone()) {/*在调用handleSimLoaded之前已经调用了handleSimAbsent, 会对mIccid赋值,所以这个判断是可以通过的。*/
updateSubscriptionInfoByIccId();
int[] subIds = mSubscriptionManager.getActiveSubscriptionIdList();
for (int subId : subIds) {
TelephonyManager tm = TelephonyManager.getDefault();
String operator = tm.getSimOperatorNumeric(subId);
slotId = SubscriptionController.getInstance().getPhoneId(subId);
if (!TextUtils.isEmpty(operator)) {
if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
MccTable.updateMccMncConfiguration(mContext, operator, false);
}
SubscriptionController.getInstance().setMccMnc(operator, subId);
} else {
logd("EVENT_RECORDS_LOADED Operator name is null");
}
String msisdn = tm.getLine1Number(subId);
ContentResolver contentResolver = mContext.getContentResolver();
if (msisdn != null) {/*更新每张卡在DB中的的number字段。*/
ContentValues number = new ContentValues(1);
number.put(SubscriptionManager.NUMBER, msisdn);
contentResolver.update(SubscriptionManager.CONTENT_URI, number,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+ Long.toString(subId), null);
}
SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
String nameToSet;
String simCarrierName = tm.getSimOperatorName(subId);
ContentValues name = new ContentValues(1);
if (subInfo != null && subInfo.getNameSource() !=
SubscriptionManager.NAME_SOURCE_USER_INPUT) {/*更新每张卡在DB中的的display_name字段。*/
if (!TextUtils.isEmpty(simCarrierName)) {
nameToSet = simCarrierName;
} else {
nameToSet = "CARD " + Integer.toString(slotId + 1);
}
name.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
logd("sim name = " + nameToSet);
contentResolver.update(SubscriptionManager.CONTENT_URI, name,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+ "=" + Long.toString(subId), null);
}
/* Update preferred network type and network selection mode on SIM change.
* Storing last subId in SharedPreference for now to detect SIM change. */
SharedPreferences sp =
PreferenceManager.getDefaultSharedPreferences(mContext);
int storedSubId = sp.getInt(CURR_SUBID + slotId, -1);
if (storedSubId != subId) {
int networkType = RILConstants.PREFERRED_NETWORK_MODE;
// Set the modem network mode
mPhone[slotId].setPreferredNetworkType(networkType, null);
Settings.Global.putInt(mPhone[slotId].getContext().getContentResolver(),
Settings.Global.PREFERRED_NETWORK_MODE + subId,
networkType);
// Only support automatic selection mode on SIM change.
mPhone[slotId].getNetworkSelectionMode(
obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE,
new Integer(slotId)));
// Update stored subId
SharedPreferences.Editor editor = sp.edit();
editor.putInt(CURR_SUBID + slotId, subId);
editor.apply();
}
// Update set of enabled carrier apps now that the privilege rules may have changed.
CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
mPackageManager, TelephonyManager.getDefault(),
mContext.getContentResolver(), mCurrentlyActiveUserId);
broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);/*广播ACTION_SIM_STATE_CHANGED*/
updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);//通知CarrierConfig加载相应资源。
}
}
}
updateSubscriptionInfoByIccId()方法:
synchronized private void updateSubscriptionInfoByIccId() {
logd("updateSubscriptionInfoByIccId:+ Start");
mSubscriptionManager.clearSubscriptionInfo();
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
mInsertSimState[i] = SIM_NOT_CHANGE;//初始值
}
int insertedSimCount = PROJECT_SIM_NUM;
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
if (ICCID_STRING_FOR_NO_SIM.equals(mIccId[i])) {
insertedSimCount--;
mInsertSimState[i] = SIM_NOT_INSERT;//没有插卡的情况
}
}
logd("insertedSimCount = " + insertedSimCount);
int index = 0;
for (int i = 0; i < PROJECT_SIM_NUM; i++) {//这段for循环用来做什么,处理卡ICCID相同的情况。
if (mInsertSimState[i] == SIM_NOT_INSERT) {
continue;//过滤掉无卡部分
}
index = 2;
for (int j = i + 1; j < PROJECT_SIM_NUM; j++) {
if (mInsertSimState[j] == SIM_NOT_CHANGE && mIccId[i].equals(mIccId[j])) {
mInsertSimState[i] = 1;
mInsertSimState[j] = index;
index++;
}
}
}
ContentResolver contentResolver = mContext.getContentResolver();
String[] oldIccId = new String[PROJECT_SIM_NUM];
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
oldIccId[i] = null;
List oldSubInfo =
SubscriptionController.getInstance().getSubInfoUsingSlotIndexWithCheck(i, false,
mContext.getOpPackageName());//从DB中查询卡槽i上所保存过的信息。
if (oldSubInfo != null && oldSubInfo.size() > 0) {
oldIccId[i] = oldSubInfo.get(0).getIccId();//取第一条
logd("updateSubscriptionInfoByIccId: oldSubId = "
+ oldSubInfo.get(0).getSubscriptionId());
if (mInsertSimState[i] == SIM_NOT_CHANGE && !mIccId[i].equals(oldIccId[i])) {//卡槽i中当前卡的iccid和之前保存的iccid不同,所以卡槽i中的SIM卡发生了改变。
mInsertSimState[i] = SIM_CHANGED;
}
if (mInsertSimState[i] != SIM_NOT_CHANGE) {//根据oldSubinfo.get(0)的subID,更新DB中的对应信息,将对应的SIM_SLOT_INDEX设置为Invalid。
ContentValues value = new ContentValues(1);
value.put(SubscriptionManager.SIM_SLOT_INDEX,
SubscriptionManager.INVALID_SIM_SLOT_INDEX);
contentResolver.update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+ Integer.toString(oldSubInfo.get(0).getSubscriptionId()), null);
}
} else {
if (mInsertSimState[i] == SIM_NOT_CHANGE) {
// no SIM inserted last time, but there is one SIM inserted now
mInsertSimState[i] = SIM_CHANGED;
}
oldIccId[i] = ICCID_STRING_FOR_NO_SIM;
logd("updateSubscriptionInfoByIccId: No SIM in slot " + i + " last time");
}
}
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
logd("updateSubscriptionInfoByIccId: oldIccId[" + i + "] = " + oldIccId[i] +
", sIccId[" + i + "] = " + mIccId[i]);
}
//check if the inserted SIM is new SIM
int nNewCardCount = 0;
int nNewSimStatus = 0;
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) {
//some special SIMs may have the same IccIds, add suffix to distinguish them
//FIXME: addSubInfoRecord can return an error.
mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i]
+ Integer.toString(mInsertSimState[i]), i);//将subscription 信息放进DB。
logd("SUB" + (i + 1) + " has invalid IccId");
} else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ {
mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i], i);//将subscription 信息放进DB。
}
if (isNewSim(mIccId[i], oldIccId)) {
nNewCardCount++;
switch (i) {
case PhoneConstants.SUB1:
nNewSimStatus |= STATUS_SIM1_INSERTED;
break;
case PhoneConstants.SUB2:
nNewSimStatus |= STATUS_SIM2_INSERTED;
break;
case PhoneConstants.SUB3:
nNewSimStatus |= STATUS_SIM3_INSERTED;
break;
//case PhoneConstants.SUB3:
// nNewSimStatus |= STATUS_SIM4_INSERTED;
// break;
}
mInsertSimState[i] = SIM_NEW;
}
}
}
for (int i = 0; i < PROJECT_SIM_NUM; i++) {/*处理到这里,所有SIM_CHANGED状态的SIM卡都是SIM_REPOSITION。*/
if (mInsertSimState[i] == SIM_CHANGED) {
mInsertSimState[i] = SIM_REPOSITION;
}
logd("updateSubscriptionInfoByIccId: sInsertSimState[" + i + "] = "
+ mInsertSimState[i]);
}
List subInfos = mSubscriptionManager.getActiveSubscriptionInfoList();
int nSubCount = (subInfos == null) ? 0 : subInfos.size();
logd("updateSubscriptionInfoByIccId: nSubCount = " + nSubCount);
for (int i=0; i < nSubCount; i++) {/*更新每张卡在DB中的的number字段。*/
SubscriptionInfo temp = subInfos.get(i);
String msisdn = TelephonyManager.getDefault().getLine1Number(
temp.getSubscriptionId());
if (msisdn != null) {
ContentValues value = new ContentValues(1);
value.put(SubscriptionManager.NUMBER, msisdn);
contentResolver.update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+ Integer.toString(temp.getSubscriptionId()), null);
}
}
// Ensure the modems are mapped correctly
mSubscriptionManager.setDefaultDataSubId(
mSubscriptionManager.getDefaultDataSubscriptionId());
SubscriptionController.getInstance().notifySubscriptionInfoChanged();
logd("updateSubscriptionInfoByIccId:- SsubscriptionInfo update complete");
}
SubscriptionMonitor.java
这个类的功能比较简单,主要是下面两点:
1.在SubscriptionController添加了Listener:mSubscriptionsChangedListener,监听subscription 的变动,并且
对外提供了registerForSubscriptionChanged(…)和unregisterForSubscriptionChanged(…)两个相关API。
2.监听了广播TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED,对应的reveiver为mDefaultDataSubscriptionChangedReceiver,并且对外提供了registerForDefaultDataSubscriptionChanged(…)和unregisterForDefaultDataSubscriptionChanged(…)两个相关API。
TelephonyProvider是在PhoneApp启动的过程中创建的, 因为TelephonyProvider将AndroidManifest.xml文件中将process配置成了"com.android.phone".
具体启动过程需要看ActivityManagerService,Activit的yThread等应用启动相关的code,不是本文重点。
...
Telephonyprovider的oncreate()方法,首先new 了一个DatabaseHelper对象。
@Override
public boolean onCreate() {
mOpenHelper = new DatabaseHelper(getContext());//new一个DatabaseHelper对象,DatabaseHelper继承自SQLiteOpenHelper。
// Call getReadableDatabase() to make sure onUpgrade is called
if (VDBG) log("onCreate: calling getReadableDatabase to trigger onUpgrade");
SQLiteDatabase db = getReadableDatabase();
......
}
创建完DatabaseHelper对象后,便调用了getReadableDatabase()方法,从这里开始,我们的注意点就要转移到DatabaseHelper和SQLiteOpenHelper这两个类中,TelephonyProvider中的getReadableDatabase()方法会调用DatabaseHelper的getReadableDatabase()方法,DatabaseHelper并没有重写getReadableDatabase()方法,所以最终会调用到SQLiteOpenHelper中的getReadableDatabase()方法,该方法又调用内部private方法getDatabaseLocked。
下面就来看看getDatabaseLocked(boolean writable)方法都做了什么工作:
private SQLiteDatabase getDatabaseLocked(boolean writable) {
if (mDatabase != null) {//刚开机,mDatabase由于没有指向有效对象,当然为null。
if (!mDatabase.isOpen()) {
// Darn! The user closed the database by calling mDatabase.close().
mDatabase = null;
} else if (!writable || !mDatabase.isReadOnly()) {//不为null时,将在database 非只读或者用于只读的情况时返回mDatabase对象。
// The database is already open for business.
return mDatabase;
}
}
if (mIsInitializing) {
throw new IllegalStateException("getDatabase called recursively");
}
SQLiteDatabase db = mDatabase;
try {
mIsInitializing = true;
if (db != null) {
if (writable && db.isReadOnly()) {
db.reopenReadWrite();
}
} else if (mName == null) {
db = SQLiteDatabase.create(null);
} else {
try {
if (DEBUG_STRICT_READONLY && !writable) {//获取用于debug的只读database。
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
} else {//打开或者创建一个可写的database。
db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
mFactory, mErrorHandler);
}
} catch (SQLiteException ex) {
if (writable) {
throw ex;
}
Log.e(TAG, "Couldn't open " + mName
+ " for writing (will try read-only):", ex);
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
}
}
onConfigure(db);
final int version = db.getVersion();//获取DB的version。
if (version != mNewVersion) {//对于刚创建的db,version的值是0,而mNewVersion是我们在创建DatabaseHelper时作为参数传入到DatabaseHelper对象的。这个参数的获取是通过
if (db.isReadOnly()) {//Telephonyprovider.gerVersion()方法,方法内会从“com.android.internal.R.xml.apns”获取version信息然后和DATABASE_VERSION做按位或运算,结果作为version。
throw new SQLiteException("Can't upgrade read-only database from version " +
db.getVersion() + " to " + mNewVersion + ": " + mName);
}
if (version > 0 && version < mMinimumSupportedVersion) {
File databaseFile = new File(db.getPath());
onBeforeDelete(db);
db.close();
if (SQLiteDatabase.deleteDatabase(databaseFile)) {
mIsInitializing = false;
return getDatabaseLocked(writable);
} else {
throw new IllegalStateException("Unable to delete obsolete database "
+ mName + " with version " + version);
}
} else {
db.beginTransaction();
try {
if (version == 0) {//刚创建的DB,调用onCreate(...)方法
onCreate(db);
} else {
if (version > mNewVersion) {//db的version比当前版本新,调用onDowngrade方法
onDowngrade(db, version, mNewVersion);
} else {//否则调用onUpgrade方法。
onUpgrade(db, version, mNewVersion);
}
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
......
}
数据表的创建在DatabaseHelper.onCreate方法里面,
@Override
public void onCreate(SQLiteDatabase db) {
if (DBG) log("dbh.onCreate:+ db=" + db);
createSimInfoTable(db);//创建siminfo表
createCarriersTable(db, CARRIERS_TABLE);//创建carriers表,存放Apn信息。
initDatabase(db);//将配置的apn数据放进数据表。
if (DBG) log("dbh.onCreate:- db=" + db);
}
siminfo表中的主要字段:
/**
* TelephonyProvider unique key column name is the subscription id.
* Type: TEXT (String)
* 主键,也就是我们所使用的subscription id。
*/
/** @hide */
public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
/**
* TelephonyProvider column name for SIM ICC Identifier
* Type: TEXT (String)
* SIM卡的ICCID。
*/
/** @hide */
public static final String ICC_ID = "icc_id";
/**
* TelephonyProvider column name for user SIM_SlOT_INDEX
* Type: INTEGER (int)
* 卡槽ID。
*/
/** @hide */
public static final String SIM_SLOT_INDEX = "sim_id";
...
/**
* TelephonyProvider column name for user displayed name.
* Type: TEXT (String)
* 可能是SIM卡里的spn,手机配置好的spn 或者CARD1/CARD2;也可能是用户输入的名称。
*/
/** @hide */
public static final String DISPLAY_NAME = "display_name";
/**
* TelephonyProvider column name for the service provider name for the SIM.
* Type: TEXT (String)
* 这个值可能是spn 也可能是registered plmn,和状态栏显示是一致的。
*/
/** @hide */
public static final String CARRIER_NAME = "carrier_name";
/**
* Default name resource
* @hide
*/
public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName;
/**
* TelephonyProvider column name for source of the user displayed name.
* Type: INT (int)
with one of the NAME_SOURCE_XXXX values below
* 用来说明display_name字段内容的来源; 分为四类:NAME_SOURCE_UNDEFINDED(-1), NAME_SOURCE_DEFAULT_SOURCE(0), NAME_SOURCE_SIM_SOURCE(1),
* NAME_SOURCE_USER_INPUT(2)
* @hide
*/
public static final String NAME_SOURCE = "name_source";
......
/**
* TelephonyProvider column name for the color of a SIM.
* Type: INTEGER (int)
*/ 为SIM卡分配的颜色值
/** @hide */
public static final String COLOR = "color";
/** @hide */
public static final int COLOR_1 = 0;
/** @hide */
public static final int COLOR_2 = 1;
/** @hide */
public static final int COLOR_3 = 2;
/** @hide */
public static final int COLOR_4 = 3;
/** @hide */
public static final int COLOR_DEFAULT = COLOR_1;
/**
* TelephonyProvider column name for the phone number of a SIM.
* Type: TEXT (String)
* 手机号码
*/
/** @hide */
public static final String NUMBER = "number";
/**
* TelephonyProvider column name for the number display format of a SIM.
* Type: INTEGER (int)
*/
/** @hide */
public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
/** @hide */
public static final int DISPLAY_NUMBER_NONE = 0;
/** @hide */
public static final int DISPLAY_NUMBER_FIRST = 1;
/** @hide */
public static final int DISPLAY_NUMBER_LAST = 2;
/** @hide */
public static final int DISPLAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST;
/**
* TelephonyProvider column name for permission for data roaming of a SIM.
* Type: INTEGER (int)
* 标识data roaming状态,默认值为DATA_ROAMING_DISABLE(0)。
*/
/** @hide */
public static final String DATA_ROAMING = "data_roaming";
/** Indicates that data roaming is enabled for a subscription */
public static final int DATA_ROAMING_ENABLE = 1;
/** Indicates that data roaming is disabled for a subscription */
public static final int DATA_ROAMING_DISABLE = 0;
/** @hide */
public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
/** @hide */
public static final int SIM_PROVISIONED = 0;
/**
* TelephonyProvider column name for the MCC associated with a SIM.
* Type: INTEGER (int)
* MCC
* @hide
*/
public static final String MCC = "mcc";
/**
* TelephonyProvider column name for the MNC associated with a SIM.
* Type: INTEGER (int)
* MNC
* @hide
*/
public static final String MNC = "mnc";
剩下的几个字段都是cell broadcast相关的了。