How to enable the PUK insertion panel in AOSP RIL

I’m moving forward on my native AOSP RIL library development: after having solved the Pin Lock State issue, I’m now facing a problem with PUK insertion.

When the SIM requires PIN insertion, after all the PIN retries have been consumed, the GUI keeps staying in PIN request mode: no PUK panel is shown, so it is impossible to unlock the SIM card.

Likewise the previous problem, this situation happens no matter what is returned by the native RIL library.

New adventures in the Java RIL code…

AOSP RIL PUK panel

AOSP RIL and the missing PUK insertion panel…

Also for this issue we need to take again a look at the native AOSP RIL call identified by the value RIL_REQUEST_GET_SIM_STATUS.

Among the fields in the structure returned by the call we can identify:

RIL_AppStatus applications[RIL_CARD_MAX_APPS];

This structure presents two interesting fields:

RIL_AppState app_state;
RIL_PinState pin1;

Analyzing the possible values of app_state:

typedef enum {
 RIL_APPSTATE_UNKNOWN = 0,
 RIL_APPSTATE_DETECTED = 1,
 RIL_APPSTATE_PIN = 2, /* If PIN1 or UPin is required */
 RIL_APPSTATE_PUK = 3, /* If PUK1 or Puk for UPin is required */
 RIL_APPSTATE_SUBSCRIPTION_PERSO = 4, /* perso_substate should be look at
                                         when app_state is assigned to this value */
 RIL_APPSTATE_READY = 5
} RIL_AppState;

it is quite clear that the value RIL_APPSTATE_PUK should be used when the SIM is PUK locked.

The possible values for pin1 are:

typedef enum {
 RIL_PINSTATE_UNKNOWN = 0,
 RIL_PINSTATE_ENABLED_NOT_VERIFIED = 1,
 RIL_PINSTATE_ENABLED_VERIFIED = 2,
 RIL_PINSTATE_DISABLED = 3,
 RIL_PINSTATE_ENABLED_BLOCKED = 4,
 RIL_PINSTATE_ENABLED_PERM_BLOCKED = 5
} RIL_PinState;

and also for this field the value to be used is clear: RIL_PINSTATE_ENABLED_BLOCKED.

A typical radio log line for this situation could be:

D/RILJ    ( 2399): [3675]< GET_SIM_STATUS IccCardState {CARDSTATE_PRESENT,PINSTATE_ENABLED_BLOCKED,num_apps=1
  gsm_id=0{APPTYPE_USIM,APPSTATE_PUK,pin1=PINSTATE_ENABLED_BLOCKED,pin2=PINSTATE_UNKNOWN},
  cdma_id=-1,ims_id=-1} [SUB0]

It looks like that things are working pretty fine on the native RIL layer, so it’s time to understand why the PUK panel is not shown at a different level.

Looking for the PUK panel…

Let’s collect the main log when supplying pin while the sim is in PIN pending state:

adb logcat -b main

There are a few interesting lines with the tag KeyguardSimPinView, e.g.:

V/KeyguardSimPinView( 2302): onSimStateChanged(subId=11,state=PIN_REQUIRED)
V/KeyguardSimPinView( 2302): Resetting state

Looking for this file in AOSP tree, it can be found in:

frameworks/base/packages/Keyguard/src/com/android/keyguard

Among the files, one called KeyguardSimPukView.java catches the attention: potentially this is involved in the PUK insertion process, so it should be investigated which part is in charge of updating the view.

The view updating process is managed by the class KeyguardViewMediator in

frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard

Looking for relevant keywords (pin, puk, sim…) brings to the function onSimStateChanged that, in case of PUK_REQUIRED, since the keyguard is already showing, resets the keyguard in the locked state. So the question becomes, who is triggering onSimStateChanged?

Adding more verbosity to the log brings to the file KeyguardUpdateMonitor.java and to the telephony intent ACTION_SIM_STATE_CHANGED.

It seems we are getting closer.

Grepping for this intent into the Java AOSP RIL points out the component that could be responsible for the triggering of this intent: IccCardProxy.

Two different functions are used for broadcasting the sim state:

broadcastInternalIccStateChangedIntent
broadcastIccStateChangedIntent

and they are called into function setExternalState, the one that is fired when the SIM application passes from APPSTATE_PIN to APPSTATE_PUK.

Into setExternalState we see this comment and code:

// For locked states, we should be sending internal broadcast.
if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(getIccStateIntentString(mExternalState))) {
   broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState),
      getIccStateReason(mExternalState));
 ...

This means that ACTION_SIM_STATE_CHANGED is never fired, but instead, it is fired an only internal intent ACTION_INTERNAL_SIM_STATE_CHANGED that cannot be caught by the KeyguardUpdateMonitor.

Adding the line

broadcastIccStateChangedIntent(getIccStateIntentString(mExternalState), getIccStateReason(mExternalState));

after broadcastInternalIccStateChangedIntent allows the PUK insertion panel to be shown.

A “feature” or a bug?

Well, to be honest I don’t know.

Maybe I’m missing something other happening after the ACTION_INTERNAL_SIM_STATE_CHANGED.

For an answer I asked in the Android Platform group, maybe someone in Google can help to shed some light…