当前位置:   article > 正文

Android源码----操作SIM卡对联系人的影响(一)_intent_value_icc_card_io_error

intent_value_icc_card_io_error

前言

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卡,会将数据库中关于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);
        }
        ......
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

设置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));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

设置好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;
                        }
                        ......
                   }
            }
   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

接下来只做了两件事情:删除数据库中存于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);
        }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

插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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

接着进入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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

其中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中
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

将从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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

到此完成了插入SIM时联系人导入到数据库的整个过程。关于页面的刷新,涉及到Loader的内容,之后再进行分析罗列。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/235615
推荐阅读
相关标签
  

闽ICP备14008679号