一、锁卡背景介绍
锁卡即SIMLock,当手机开机启动或者插入SIM卡时,手机modem侧预置在NV项中的配置信息会与SIM卡中的信息做比对,检测是否匹配。若匹配,则SIM卡可以正常使用。若不匹配,则SIM卡相关功能均无法正常使用,例如拨打电话、发送短信及上网等;或者是只能注册2G网,不能注册4G。
锁卡的目的:一些运营商会要求控制某一类卡的使用,从而保护自己的利益(运营商定制机)
SIMLock锁和图案锁,数字密码锁,PIN码锁,PUK锁一样,是Keyguard模块中的一种锁。
二、锁卡的需求
锁卡的需求方式有7种之多,常见的有NP锁,NS锁,CP锁,SP锁等
从安卓机器来看,目前最常用的是SP锁(MCC/MNC),本文将解析SP锁的加锁流程
三、锁卡流程解析
1.首先modem侧检测SIM卡的配置信息并与之作比对,若匹配则继续加载SIM卡,若不匹配,则上报加锁信息
2.RIL层检测到modem上报的加锁信息,然后发送给framework层,最终在AP层监测到事件:
3.AP层显示出锁卡界面,要求用户输入解锁码进行解锁
四、相关流程图
1.加锁流程图
2.解锁流程图
五、上层UI界面流程
1.与锁卡界面相关类主要有:1.KeyguardUpdateMonitor:监听各种广播,比如时间、SIM卡状态、电池电量等。
2.KeyguardSimPinPukMeView:锁卡UI界面的显示类。如下图
3.KeyguardViewMediator:在SIM卡被锁卡之后的dialog显示。
4.KeyguardUtils :工具类。
2.他们之间的关系:
他们采用观察者模式,KeyguardUpdateMonitor是所有类的监控器,KeyguardSimPinPukMeView和KeyguardViewMediator通过registerCallback注册监听。
3.锁卡UI界面的逻辑流程
KeyguardUpdateMonitor ->onReceive()监听SIM卡变化广播,对SIM的状态进行判断,最终会回调之前registerCallback注册的监听接口.KeyguardSimPinPukMeView ->onSimStateChangedUsingPhoneId
注释:SIM卡的状态:IccCardConstants->
UNKNOWN, /** ordinal(0) == {@See TelephonyManager#SIM_STATE_UNKNOWN} */
ABSENT, /** ordinal(1) == {@See TelephonyManager#SIM_STATE_ABSENT} */
PIN_REQUIRED, /** ordinal(2) == {@See TelephonyManager#SIM_STATE_PIN_REQUIRED} */
PUK_REQUIRED, /** ordinal(3) == {@See TelephonyManager#SIM_STATE_PUK_REQUIRED} */
NETWORK_LOCKED, /** ordinal(4) == {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED} */
READY, /** ordinal(5) == {@See TelephonyManager#SIM_STATE_READY} */
NOT_READY, /** ordinal(6) == {@See TelephonyManager#SIM_STATE_NOT_READY} */
PERM_DISABLED, /** ordinal(7) == {@See TelephonyManager#SIM_STATE_PERM_DISABLED} */
CARD_IO_ERROR, /** ordinal(8) == {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} */
CARD_RESTRICTED;/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */
这些状态的意思,具体的可以查看TelephonyManager里面的注释。
4.一些UI界面的显示
这个逻辑界面的显示是在KeyguardSimPinPukMeView ->resetState
if (count < 2) {
if (simState == IccCardConstants.State.PIN_REQUIRED) {
msg = rez.getString(R.string.kg_sim_pin_instructions);
mUnlockEnterState = STATE_ENTER_PIN;
} else if (simState == IccCardConstants.State.PUK_REQUIRED) {
msg = rez.getString(R.string.kg_puk_enter_puk_hint);
mUnlockEnterState = STATE_ENTER_PUK;
} else if ((IccCardConstants.State.NETWORK_LOCKED == simState) &&
KeyguardUtils.isMediatekSimMeLockSupport()) {
int category = mUpdateMonitor.getSimMeCategoryOfPhoneId(mPhoneId);
msg = rez.getString(R.string.simlock_entersimmelock)
+ strLockName[category]
+ getRetryMeString(mPhoneId);
mUnlockEnterState = STATE_ENTER_ME;
}
} else {
看这段代码就知道界面的显示会有两个分支,一个是count>=2,一个是count<2.count是什么鬼呢?它表示机器可以支持几个卡槽。也就我们说的单卡还是双卡。MTK平板的是双卡曹,所以走else。Wrong Code Enter SIM ME lock 由R.string.simlock_entersimmelock定义。[NP] 由strLockName [category]定义。
strLockName = {" [NP]", " [NSP]", " [SP]", " [CP]", " [SIMP]"}; 它表示锁卡类型。Remains:2 由getRetryMeString(mPhoneId)定义,最开始是5次随着输入错误次数最后会变成0,此卡也就被锁了。那么次数是如何决定的呢?一开始初始化 KeyguardUpdateMonitor ->DEFAULT_ME_RETRY_COUNT = 5 后面由run方法校准。
public void run() {
try {
mSimMeCategory.put(simArgs.phoneId, simArgs.simMECategory);
Log.d(TAG, "queryNetworkLock, phoneId =" + simArgs.phoneId + ", simMECategory ="
+ simArgs.simMECategory);
if (simArgs.simMECategory < 0 || simArgs.simMECategory > 5) {
return;
}
int subId = KeyguardUtils.getSubIdUsingPhoneId(simArgs.phoneId) ;
Bundle bundle = IMtkTelephonyEx.Stub.asInterface(
ServiceManager.getService("phoneEx"))
.queryNetworkLock(subId, simArgs.simMECategory);
boolean query_result = bundle.getBoolean(QUERY_SIMME_LOCK_RESULT, false);
Log.d(TAG, "queryNetworkLock, " + "query_result =" + query_result);
if (query_result) {
mSimMeLeftRetryCount.put(simArgs.phoneId,
bundle.getInt(SIMME_LOCK_LEFT_COUNT, 5));//锁卡次数
} else {
Log.e(TAG, "queryIccNetworkLock result fail");
}
mHandler.sendMessage(mHandler.obtainMessage(MSG_SIM_STATE_CHANGE, simArgs));
} catch (Exception e) {
Log.e(TAG, "queryIccNetworkLock got exception: " + e.getMessage());
}
那么每次输入错误它由如何递减呢?KeyguardSimPinPukMeView ->minusRetryMeCount 调用KeyguardUpdateMonitor ->minusSimMeLeftRetryCountOfPhoneId(phoneId)。
KeyguardPinBasedInputView是所有按键的逻辑处理类,
mOkButton = findViewById(R.id.key_enter);
if (mOkButton != null) {
mOkButton.setOnTouchListener(this);
mOkButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mPasswordEntry.isEnabled()) {
verifyPasswordAndUnlock();
}
}
});
会调用verifyPasswordAndUnlock(),verifyPasswordAndUnlock()是一个抽象方法最终由KeyguardSimPinPukMeView 实现
protected void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText().toString();//获取输入密码。
///M: here only for PIN code
if ((false == validatePin(entry, false)//密码格式判断是否正确) &&
(mUpdateMonitor.getSimStateOfPhoneId(mPhoneId) == IccCardConstants.State.PIN_REQUIRED ||
(mUpdateMonitor.getSimStateOfPhoneId(mPhoneId) == IccCardConstants.State.NETWORK_LOCKED)
&& KeyguardUtils.isMediatekSimMeLockSupport())) {
// otherwise, display a message to the user, and don't submit.
if (mUpdateMonitor.getSimStateOfPhoneId(mPhoneId) ==
IccCardConstants.State.PIN_REQUIRED) {
mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint);
} else {
// hint to enter 4-8 digits for network_lock mode
mSecurityMessageDisplay.setMessage(R.string.keyguard_code_length_prompt);
}
mPasswordEntry.reset(true, true);
mPasswordEntry.setEnabled(true);
mCallback.userActivity();
return;
}
mPasswordEntry.setEnabled(true);//对输入的密码进行验证
dealWithPinOrPukUnlock();
}
private void dealWithPinOrPukUnlock() {
if (mUpdateMonitor.getSimStateOfPhoneId(mPhoneId) == IccCardConstants.State.PIN_REQUIRED) {
Log.d(TAG, "onClick, checkPin, mPhoneId=" + mPhoneId);
checkPin(mPhoneId);
} else if (mUpdateMonitor.getSimStateOfPhoneId(mPhoneId) ==
IccCardConstants.State.PUK_REQUIRED) {
Log.d(TAG, "onClick, checkPuk, mPhoneId=" + mPhoneId);
checkPuk(mPhoneId);
} else if ((mUpdateMonitor.getSimStateOfPhoneId(mPhoneId) ==
IccCardConstants.State.NETWORK_LOCKED)
&& KeyguardUtils.isMediatekSimMeLockSupport()) {
Log.d(TAG, "onClick, checkMe, mPhoneId=" + mPhoneId);
checkMe(mPhoneId);
} else {
Log.d(TAG, "wrong status, mPhoneId=" + mPhoneId);
}
}
SIM的状态是IccCardConstants.State.NETWORK_LOCKED,所以走checkMe。
private void checkMe(int phoneId) {
getSimUnlockProgressDialog().show();
if (!mSimCheckInProgress) {
mSimCheckInProgress = true; // there should be only one
new CheckSimMe(mPasswordEntry.getText().toString(), phoneId) {
void onSimMeCheckResponse(final int ret) {
Log.d(TAG, "checkMe onSimChangedResponse, ret = " + ret);
if (VERIFY_RESULT_PASS == ret) {
Log.d(TAG, "checkMe VERIFY_RESULT_PASS == ret(we had sent runnable before");
mUpdateMonitor.reportSimUnlocked(mPhoneId);
mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
} else if (VERIFY_INCORRECT_PASSWORD == ret) {
mSb.delete(0, mSb.length());
minusRetryMeCount(mPhoneId);//减少错误次数
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.hide();
}
if (mUnlockEnterState == STATE_ENTER_ME) {
if (0 == getRetryMeCount(mPhoneId)) { //permanently locked
setInputInvalidAlertDialog(mContext.getText(
R.string.simlock_slot_locked_message), true);//弹出对话框
mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
} else {
int category = mUpdateMonitor.getSimMeCategoryOfPhoneId(mPhoneId);
mSb.append(mContext.getText(R.string.keyguard_wrong_code_input));
mSb.append(mContext.getText(R.string.simlock_entersimmelock));
mSb.append(strLockName[category] + getRetryMeString(mPhoneId));
}
Log.d(TAG, "checkMe() - VERIFY_INCORRECT_PASSWORD == ret, "
+ "mSecurityMessageDisplay.setMessage = " + mSb.toString()) ;
mSecurityMessageDisplay.setMessage(mSb.toString());
mPasswordEntry.reset(true, true);//刷新界面
}
} else if (VERIFY_RESULT_EXCEPTION == ret) {
if (mSimUnlockProgressDialog != null) {
mSimUnlockProgressDialog.hide();
}
setInputInvalidAlertDialog("Exception happen, fail to unlock", true);
mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
}
mCallback.userActivity();
mSimCheckInProgress = false;
}
} .start();
}
SIM锁卡后,每次开机都会谭一个dialog
KeyguardViewMediator ->onSimStateChangedUsingPhoneId
if (IccCardConstants.State.NETWORK_LOCKED == simState
&& 0 == mUpdateMonitor.getSimMeLeftRetryCountOfPhoneId(phoneId)
) {
//add xiao
if(LOCK_TIMES<2){
LOCK_TIMES++;
Log.d(TAG, "SIM ME lock retrycount is 0, only to show dialog");
mDialogManager.requestShowDialog(new MeLockedDialogCallback());//锁卡后弹出的对话框,弹出的对话框次数依据回调onSimStateChangedUsingPhoneId次数决定。
}
break;
}