独角兽企业重金招聘Python工程师标准>>>
PIN,全称Personal Identification Number,即个人识别码,用来认证使用者的身份。
当用户启用PIN后,手机在加载SIM卡时(开机或插卡)就会要求用户输入PIN,这时用户有3次输入PIN的机会,
但若3次都输入错误的话,就会要求输入PUK,即PIN Unlocking Key,PUK是PIN的解锁码,需要注意的是,
PUK总共只有10次输入机会,也就是说,只要输入了10次错误的PUK,该SIM卡就报废了,而这里所说的10次
并非指连续10次,而是只要输错一次,SIM卡内部计数器就减1,即使输入正确的PUK后,计数器的值也不会被
恢复,直到计数器减为0后,该卡报废。
PIN的种类
Universal PIN:多个Application共用一个PIN
Application PIN:Application自己的PIN
Local PIN:某个DF特有的PIN
手机在读取启用了PIN的SIM卡时,读到的状态为PIN,可通过如下的RIL log查看APPSTATE字段。
RILJ : [3775]< GET_SIM_STATUS IccCardState {CARDSTATE_PRESENT,PINSTATE_ENABLED_NOT_VERIFIED,num_apps=1,gsm_id=0{APPTYPE_USIM,APPSTATE_PIN,pin1=PINSTATE_ENABLED_NOT_VERIFIED,pin2=PINSTATE_ENABLED_NOT_VERIFIED},cdma_id=-1,ims_id=-1} [SUB0]
而APPSTATE在IccCardApplicationStatus.AppState进行了定义,一共有以下几种状态:
public enum AppState{
APPSTATE_UNKNOWN,
APPSTATE_DETECTED,
APPSTATE_PIN,
APPSTATE_PUK,
APPSTATE_SUBSCRIPTION_PERSO,
APPSTATE_READY;
}
由于之前在UICC开机初始化的流程中已经对卡状态上报流程进行了一些分析,因此我们在这里就只简单说明下。
首先,在IccCardProxy中会注册监听EVENT_ICC_LOCKED事件。
private void registerUiccCardEvents() {
......
if (mUiccApplication != null) {
......
mUiccApplication.registerForLocked(this, EVENT_ICC_LOCKED, null);
......
}
......
}
当modem上报PIN_LOCKED后,在UiccCardApplication.update()方法中,会执行通知监听者的操作。
public void update (IccCardApplicationStatus as, Context c, CommandsInterface ci) {
......
if (mAppState != oldAppState) {
......
notifyPinLockedRegistrantsIfNeeded(null);
......
}
然后就是在IccCardProxy中处理EVENT_ICC_LOCKED事件。
public void handleMessage(Message msg) {
......
case EVENT_ICC_LOCKED:
processLockedState();
break;
......
}
将ExternalState设置为PIN_REQUIRED,为啥叫ExternalState?我也不知道!
private void processLockedState() {
......
AppState appState = mUiccApplication.getState();
switch (appState) {
case APPSTATE_PIN:
mPinLockedRegistrants.notifyRegistrants();
setExternalState(State.PIN_REQUIRED);
break;
......
}
private void setExternalState(State newState) {
setExternalState(newState, false);
}
对于ICC_LOCKED这种状态,我们需要发送InternalIccStateChanged的广播,为啥又是Internal?跟ExternalState啥关系?不知道!
private void setExternalState(State newState, boolean override) {
......
// For locked states, we should be sending internal broadcast.
if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(getIccStateIntentString(mExternalState))) {
broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState),
getIccStateReason(mExternalState));
} else {
broadcastIccStateChangedIntent(getIccStateIntentString(mExternalState),
getIccStateReason(mExternalState));
}
......
}
我们来看看广播的内容,包含了ICC_STATE、LOCKED_REASON,以及PhoneId。
private void broadcastInternalIccStateChangedIntent(String value, String reason) {
......
Intent intent = new Intent(ACTION_INTERNAL_SIM_STATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
intent.putExtra(PhoneConstants.PHONE_KEY, mPhoneId); // SubId may not be valid.
log("Sending intent ACTION_INTERNAL_SIM_STATE_CHANGED" + " for mPhoneId : " + mPhoneId);
ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
}
}
接下来我们再来看看接受广播的地方,在SubscriptionInfoUpdater中,
发送消息让Handler处理EVENT_SIM_LOCKED事件。
private final BroadcastReceiver sReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
......
} else if (action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) {
if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(simStatus)) {
String reason = intent.getStringExtra(
IccCardConstants.INTENT_KEY_LOCKED_REASON);
sendMessage(obtainMessage(EVENT_SIM_LOCKED, slotIndex, -1, reason));
......
}
logd("[Receiver]-");
}
};
public void handleMessage(Message msg) {
......
case EVENT_SIM_LOCKED:
handleSimLocked(msg.arg1, (String) msg.obj);
break;
......
}
接下来会去读取IccId。
private void handleSimLocked(int slotId, String reason) {
......
if (fileHandler != null) {
String iccId = mIccId[slotId];
if (iccId == null) {
logd("Querying IccId");
fileHandler.loadEFTransparent(IccConstants.EF_ICCID,
obtainMessage(EVENT_SIM_LOCKED_QUERY_ICCID_DONE,
new QueryIccIdUserObj(reason, slotId)));
......
}
读取完IccId后,会再发送广播TelephonyIntents.ACTION_SIM_STATE_CHANGED。
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_SIM_LOCKED_QUERY_ICCID_DONE: {
......
broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED,
uObj.reason);
......
}
private void broadcastSimStateChanged(int slotId, String state, String reason) {
Intent i = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
......
i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
i.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
i.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, state);
i.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
SubscriptionManager.putPhoneIdAndSubIdExtra(i, slotId);
logd("Broadcasting intent ACTION_SIM_STATE_CHANGED " + state + " reason " + reason +
" for mCardIndex: " + slotId);
ActivityManager.broadcastStickyIntent(i, UserHandle.USER_ALL);
rebroadcastIntentsOnUnlock.put(slotId, i);
}
再往后就是SystemUI处理广播,然后弹出输入PIN的界面。
在KeyguardSimPinView中,当用户输入PIN并点击确定后,就会调用PhoneInterfaceManager.supplyPinReportResultForSubscriber()
将PIN发送给modem尝试解锁。
public int[] supplyPinReportResultForSubscriber(int subId, String pin) {
enforceModifyPermission();
final UnlockSim checkSimPin = new UnlockSim(getPhone(subId).getIccCard());
checkSimPin.start();
return checkSimPin.unlockSim(null, pin);
}
继续将请求发送给IccCard,完成后会处理SUPPLY_PIN_COMPLETE事件。
synchronized int[] unlockSim(String puk, String pin) {
......
Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
if (puk == null) {
mSimCard.supplyPin(pin, callback);
} else {
......
}
......
}
而这里的IccCard是通过GsmCdmaPhone获取到的,实际上是一个IccCardProxy对象,
因此实际调用的方法是IccCardProxy的supplyPin().
public void supplyPin(String pin, Message onComplete) {
synchronized (mLock) {
if (mUiccApplication != null) {
mUiccApplication.supplyPin(pin, onComplete);
......
}
继续到UiccApplication处理,通过调用RIL的接口将PIN发送给modem。
public void supplyPin (String pin, Message onComplete) {
synchronized (mLock) {
mCi.supplyIccPinForApp(pin, mAid, mHandler.obtainMessage(EVENT_PIN1_PUK1_DONE,
onComplete));
}
}
modem返回的结果最终会到达PhoneInterfaceManager,并将结果返回给SystemUI。
public void handleMessage(Message msg) {
AsyncResult ar = (AsyncResult) msg.obj;
switch (msg.what) {
case SUPPLY_PIN_COMPLETE:
Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
synchronized (UnlockSim.this) {
mRetryCount = msg.arg1;
if (ar.exception != null) {
if (ar.exception instanceof CommandException &&
((CommandException)(ar.exception)).getCommandError()
== CommandException.Error.PASSWORD_INCORRECT) {
mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
} else {
mResult = PhoneConstants.PIN_GENERAL_FAILURE;
}
} else {
mResult = PhoneConstants.PIN_RESULT_SUCCESS;
}
mDone = true;
UnlockSim.this.notifyAll();
}
break;
}
}
而SIM卡解锁后,就会开始进行正常的初始化流程了。