赞
踩
SIM(Subscriber Identification Module )卡:也称为用户身份识别卡、智能卡,GSM数字移动电话机必须装上此卡方能使用。SIM卡有很多功能,其中有一项是存储用户相关数据,这些数据主要分为五类:第一类是固定存放的数据,这类数据在ME(Mobile Equipment)被出售之前由SIM卡中心写入,包括国际移动用户识别号(IMSI)、鉴权密钥(KI)等;第二类是暂时存放的有关网络的数据,如位置区域识别码(LAI)、移动用户暂时识别码(TMSI)、禁止接入的公共电话网代码等;第三类是相关的业务代码,如个人识别码(PIN)、解锁码(PUK)、计费费率等;第四类是电话号码薄,是用户随时存入的电话号码。第五类是短信息。本文我们主要讨论SIM卡中存储电话号码簿的情况,一般的SIM卡的IC芯片中,有128kb的存储容量,可供存储500组电话号码及其对应的姓名文字,可以在源码中定义(MAX_OPERATIONS_SIZE)。
由于SIM卡的存储量有限,所存储的联系人字段会有一定的限制,在AOSP(Android Open-Source Project)原生中,可存储的字段有:“name”、“number”、“emails”、"_id"分别表示单个联系人的姓名、电话号码、邮箱号码、id;在高通平台中,可存储的多了一个字段:“name”、“number”、“emails”、“anrs”、"_id"其中"anrs"表示单个联系人的第二个电话号码。
操作SIM卡中的联系人主要分为三种情况:热插拔SIM卡、在Contacts的Setting中Import/Export SIM卡中的联系人、开机自动导入SIM卡中联系人信息。
本文涉及的源码解析是基于android8平台,且仅针对一张SIM卡的操作进行分析
每次热插拔SIM卡,会将数据库中关于SIM卡的联系人进行增删,SimStateReceiver通过接收RIL层上报的SIM卡状态改变的广播判断当前SIM卡的状态,并启动SimContactsService进行对应的增删操作,值得注意的一点是在AOSP中并没有SimStateReceiver和SimContactsService等相关代码,需要依赖高通或MTK提供的相关patch。以高通项目为例,路径大致为(vendor/qcom/proprietary/telephony-apps/SimContacts)。
拔SIM流程图如下:
在热拔SIM卡时,会收到关于SIM状态改变的广播(android.intent.action.SIM_STATE_CHANGED),此时SIM卡的状态为ABSENT。在SimStateReceiver收到广播之后,对SIM卡的状态进行设置:
public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); mContext = context; if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) { final int slotId = intent.getIntExtra(PhoneConstants.SLOT_KEY, SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubscriptionId())); final String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); final int simState; ...... else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra) || IccCardConstants.INTENT_VALUE_ICC_UNKNOWN.equals(stateExtra) || IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) { simState = SimContactsConstants.SIM_STATE_ERROR; } sendSimState(slotId, simState); } ...... }
设置SIM卡的状态为 SIM_STATE_ERROR,接着调用sendSimState()方法。
private void sendSimState(int slotId, int state) {
Bundle args = new Bundle();
args.putInt(PhoneConstants.SLOT_KEY, slotId);
args.putInt(SimContactsService.OPERATION, SimContactsService.MSG_SIM_STATE_CHANGED);
args.putInt(SimContactsService.SIM_STATE, state);
mContext.startService(new Intent(mContext, SimContactsService.class)
.putExtras(args));
}
设置好Bundle直接启动服务SimContactsService,在服务中获取Bundle的信息,交给handle来处理,MSG.what=MSG_SIM_STATE_CHANGED:
mServiceHandler = new Handler() { @Override public void handleMessage(Message msg) { Bundle args = (Bundle) msg.obj; switch (msg.what) { ...... case MSG_SIM_STATE_CHANGED: final int state = args.getInt(SimContactsService.SIM_STATE); int slotId = args.getInt(PhoneConstants.SLOT_KEY, SubscriptionManager.getPhoneId(SubscriptionManager. getDefaultVoicePhoneId())); Log.d(TAG, "on sim state changed event, state:" + state + ", slot:" + slotId); if (state == mSimState[slotId] && mInvalidSubInfo[slotId] == false) { break; } mSimState[slotId] = state; if (mSimState[slotId] == SimContactsConstants.SIM_STATE_NOT_READY || mSimState[slotId] == SimContactsConstants.SIM_STATE_ERROR) { // To handle card absent/error, hot swap related cases. deleteDatabaseSimContacts(slotId); deleteSimAccount(slotId); break; } ...... } } }
接下来只做了两件事情:删除数据库中存于SIM卡的联系人;清除SIMAccount。
private void deleteDatabaseSimContacts(int slotId) { if (!hasContactsProviderExist()) { Log.d(TAG, "contacts content provider not exist"); return; } getContentResolver().delete(ContactsContract.RawContacts.CONTENT_URI, SIM_DATABASE_SELECTION, getSimAccountDBSelectArgs(slotId)); Log.d(TAG, "deleteDatabaseSimContacts"); } private void deleteSimAccount(int slotId) { if (findAccount(SimContactsConstants.getSimAccountName(slotId), SimContactsConstants.ACCOUNT_TYPE_SIM) != null) { accountManager.removeAccount( new Account(SimContactsConstants.getSimAccountName(slotId), SimContactsConstants.ACCOUNT_TYPE_SIM), null, null); } }
插SIM卡流程如下
在启动SimContactsService服务之前的流程与拔SIM卡相同,唯一的区别是此时SIM的状态为READY,依然收到SIM状态改变的广播,在handle的处理过程中,由于 MSG_SIM_STATE_CHANGED分支中未有满足break的条件,因此顺延处理下一条case:
case MSG_SIM_REFRESH: Log.d(TAG, "on sim refresh event"); int sub = args.getInt(PhoneConstants.SLOT_KEY, SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubscriptionId())); if (mSimState[sub] == SimContactsConstants.SIM_STATE_READY) { if (!isSimOperationInprocess[sub]) { handleSimOp(sub); } else { Log.d(TAG, "queue refresh sim op"); refreshQueue.put(sub, MSG_SIM_REFRESH); } } else if (mSimState[sub] == SimContactsConstants.SIM_STATE_ERROR) { handleNoSim(sub); } break; }
接着进入handleSimOp方法,主要也是做两件事情:删除数据库中关于SIM卡的联系人;查询当前SIM卡中的联系人并存入数据库。
private void querySimContacts(int slotId) { Uri uri = null; if (!isMultiSimEnabled()) { uri = Uri.parse("content://icc/adn"); //单卡URI } else { SubscriptionInfo subInfo = SubscriptionManager.from(mContext) .getActiveSubscriptionInfoForSimSlotIndex(slotId); if (subInfo != null) { mInvalidSubInfo[slotId] = false; uri = Uri.parse("content://icc/adn/subId/" + subInfo.getSubscriptionId()); //多卡URI } else { mInvalidSubInfo[slotId] = true; return; } } mQuerySimHandler[slotId].startQuery(QUERY_TOKEN, null, uri, null, null, null, null); }
其中mQuerySimHandler继承了AsyncQueryHandler进行异步查询,将查询的cursor存放在mSimCursor中,开启新线程,先为SIM创建Account(createSimAccountIfNotExist()),并将所有的SIM卡联系人添加到数据库中。
private static void actuallyImportOneSimContact( final Cursor cursor, final ContentResolver resolver, Account account, ArrayList<ContentProviderOperation> operationList) { final String name = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_NAME));//获取单个联系人的姓名 final String phoneNumber = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_NUMBER));//获取单个联系人的电话号码 String emailAddresses = null; int ref = operationList.size(); try { emailAddresses = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_NAME_EMAIL));//获取单个联系人的邮箱地址 } catch (IllegalArgumentException e) { } String anrs = null; try { anrs = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_NAME_ANR));//获取单个联系人的another电话号码 } catch (IllegalArgumentException e) { } if (DBG) Log.d(TAG,String.format("name: %s, number: %s, anrs: %s, emails: %s", name, phoneNumber, anrs, emailAddresses)); final String[] emailAddressArray; final String[] anrArray; if (!TextUtils.isEmpty(emailAddresses)) { emailAddressArray = emailAddresses.split(","); } else { emailAddressArray = null; } if (!TextUtils.isEmpty(anrs)) { anrArray = anrs.split(":"); } else { anrArray = null; } ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);//创建INSERT事务 builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED); if (account != null) { builder.withValue(RawContacts.ACCOUNT_NAME, account.name);//将此时插入的联系人Account设为SIM builder.withValue(RawContacts.ACCOUNT_TYPE, account.type); } operationList.add(builder.build()); if (!TextUtils.isEmpty(name)) { builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, ref); builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); builder.withValue(StructuredName.GIVEN_NAME, name); operationList.add(builder.build());//将姓名相关参数添加到operationList中 } if (!TextUtils.isEmpty(phoneNumber)) { builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); builder.withValueBackReference(Phone.RAW_CONTACT_ID, ref); builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE); builder.withValue(Phone.NUMBER, phoneNumber); builder.withValue(Data.IS_PRIMARY, 1); operationList.add(builder.build());//将联系方式相关参数添加到operationList中 } if (anrArray != null) { for (String anr : anrArray) { builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); builder.withValueBackReference(Phone.RAW_CONTACT_ID, ref); builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); builder.withValue(Phone.TYPE, Phone.TYPE_HOME); builder.withValue(Phone.NUMBER, anr); operationList.add(builder.build());//将another联系方式相关参数添加到operationList中 } } if (emailAddressArray != null) { for (String emailAddress : emailAddressArray) { builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); builder.withValueBackReference(Email.RAW_CONTACT_ID, ref); builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); builder.withValue(Email.TYPE, Email.TYPE_MOBILE); builder.withValue(Email.ADDRESS, emailAddress); operationList.add(builder.build());//将Email相关参数添加到operationList中 } } }
将从SIM卡中查询到的联系人的相关内容通过上述方法添加到ArrayList< ContentProviderOperation> operationList列表中,调用ContentResolver.applyBatch()将创建的Insert方法进行事务处理。在线程中处理完上述内容之后,需要做好收尾工作,关闭cursor等操作,防止内存泄露的问题。
finally {
if (mSimCursor[mSlotId] != null
&& !mSimCursor[mSlotId].isClosed()) {
mSimCursor[mSlotId].close();
mSimCursor[mSlotId] = null;
}
isSimOperationInprocess[mSlotId] = false;
sendPendingSimRefreshUpdateMsg(mSlotId);
}
到此完成了插入SIM时联系人导入到数据库的整个过程。关于页面的刷新,涉及到Loader的内容,之后再进行分析罗列。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。