Few PIN/PUK fixes

Following changes have been made as part of this:
-> Changes done to display retry counter on wrong entry of
   PIN1,and message to indicate Code accepted/PIN1 blocked
   during PIN1 verification as per certain carrier requirements.
-> The current APIs that are used to verify the PIN and PUK only convey
   whether the operation succeeded or failed. As a result on ANY failure
   clients ask the user to re-enter the PIN.
   Add 2 new APIs that report the actual error code and returns the
   number of attempts remaing in case of failure.
-> FDN Service state was derived based on the state of PIN2. Update the
   state of FDN service based on the FACILTY_LOCK messages instead.
-> Change the default value of function getIccLockEnabled to false.
   When sim is deactivated/absent & user navigates to
   Settings->Security->Set up SIM/RUIM card lock,
   checkbox for "Lock Sim Card" option should be
   unchecked by default.
-> PIN1 can be changed only after enabling SIM lock. RIL returns
   REQUEST_NOT_SUPPORTED error if user tries to change PIN1 without
   enabling SIM lock.
   Handle the error and display appropriate message when trying to
   change PIN1 using MMI code.
-> Added MMI support for change PIN1/PIN2 and unblocking PIN2

Bug: 9928717
Change-Id: I73718c9e6a8aa7244097e0dd4593a6226ff0ac08
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 670d976..a83a6c3 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -574,6 +574,9 @@
      *  This exception is CommandException with an error of PASSWORD_INCORRECT
      *  if the password is incorrect
      *
+     *  ar.result is an optional array of integers where the first entry
+     *  is the number of attempts remaining before the ICC will be PUK locked.
+     *
      * ar.exception and ar.result are null on success
      */
 
@@ -590,6 +593,9 @@
      *  This exception is CommandException with an error of PASSWORD_INCORRECT
      *  if the password is incorrect
      *
+     *  ar.result is an optional array of integers where the first entry
+     *  is the number of attempts remaining before the ICC will be PUK locked.
+     *
      * ar.exception and ar.result are null on success
      */
 
@@ -604,6 +610,9 @@
      *  This exception is CommandException with an error of PASSWORD_INCORRECT
      *  if the password is incorrect
      *
+     *  ar.result is an optional array of integers where the first entry
+     *  is the number of attempts remaining before the ICC is permanently disabled.
+     *
      * ar.exception and ar.result are null on success
      */
 
@@ -614,12 +623,14 @@
      *
      *  AID (Application ID), See ETSI 102.221 8.1 and 101.220 4
      *
-     *  returned message
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
      *  This exception is CommandException with an error of PASSWORD_INCORRECT
      *  if the password is incorrect
      *
+     *  ar.result is an optional array of integers where the first entry
+     *  is the number of attempts remaining before the ICC is permanently disabled.
+     *
      * ar.exception and ar.result are null on success
      */
 
@@ -636,6 +647,9 @@
      *  This exception is CommandException with an error of PASSWORD_INCORRECT
      *  if the password is incorrect
      *
+     *  ar.result is an optional array of integers where the first entry
+     *  is the number of attempts remaining before the ICC will be PUK locked.
+     *
      * ar.exception and ar.result are null on success
      */
 
@@ -654,6 +668,9 @@
      *  This exception is CommandException with an error of PASSWORD_INCORRECT
      *  if the password is incorrect
      *
+     *  ar.result is an optional array of integers where the first entry
+     *  is the number of attempts remaining before the ICC will be PUK locked.
+     *
      * ar.exception and ar.result are null on success
      */
 
@@ -670,6 +687,9 @@
      *  This exception is CommandException with an error of PASSWORD_INCORRECT
      *  if the password is incorrect
      *
+     *  ar.result is an optional array of integers where the first entry
+     *  is the number of attempts remaining before the ICC is permanently disabled.
+     *
      * ar.exception and ar.result are null on success
      */
 
@@ -688,11 +708,15 @@
      *  This exception is CommandException with an error of PASSWORD_INCORRECT
      *  if the password is incorrect
      *
+     *  ar.result is an optional array of integers where the first entry
+     *  is the number of attempts remaining before the ICC is permanently disabled.
+     *
      * ar.exception and ar.result are null on success
      */
 
     void supplyIccPuk2ForApp(String puk2, String newPin2, String aid, Message result);
 
+    // TODO: Add java doc and indicate that msg.arg1 contains the number of attempts remaining.
     void changeIccPin(String oldPin, String newPin, Message result);
     void changeIccPinForApp(String oldPin, String newPin, String aidPtr, Message result);
     void changeIccPin2(String oldPin2, String newPin2, Message result);
diff --git a/src/java/com/android/internal/telephony/IccCard.java b/src/java/com/android/internal/telephony/IccCard.java
index 1ec4f18..e5b34e2 100644
--- a/src/java/com/android/internal/telephony/IccCard.java
+++ b/src/java/com/android/internal/telephony/IccCard.java
@@ -108,6 +108,13 @@
     public void supplyPuk2 (String puk2, String newPin2, Message onComplete);
 
     /**
+     * Check whether fdn (fixed dialing number) service is available.
+     * @return true if ICC fdn service available
+     *         false if ICC fdn service not available
+    */
+    public boolean getIccFdnAvailable();
+
+    /**
      * Supply Network depersonalization code to the RIL
      */
     public void supplyNetworkDepersonalization (String pin, Message onComplete);
@@ -214,4 +221,14 @@
      * @return true if a ICC card is present
      */
     public boolean hasIccCard();
+
+    /**
+     * @return true if ICC card is PIN2 blocked
+     */
+    public boolean getIccPin2Blocked();
+
+    /**
+     * @return true if ICC card is PUK2 blocked
+     */
+    public boolean getIccPuk2Blocked();
 }
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index a9b2c29..b189df1 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -193,7 +193,7 @@
 
         if (RIL.RILJ_LOGD) Rlog.d(LOG_TAG, serialString() + "< "
             + RIL.requestToString(mRequest)
-            + " error: " + ex);
+            + " error: " + ex + " ret=" + RIL.retToString(mRequest, ret));
 
         if (mResult != null) {
             AsyncResult.forMessage(mResult, ret, ex);
@@ -2501,7 +2501,7 @@
         return rr;
     }
 
-    private String
+    static String
     retToString(int req, Object ret) {
         if (ret == null) return "";
         switch (req) {
diff --git a/src/java/com/android/internal/telephony/cdma/CDMAPhone.java b/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 38524df..e0de106 100644
--- a/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -575,12 +575,12 @@
 
     @Override
     public boolean handlePinMmi(String dialString) {
-        CdmaMmiCode mmi = CdmaMmiCode.newFromDialString(dialString, this);
+        CdmaMmiCode mmi = CdmaMmiCode.newFromDialString(dialString, this, mUiccApplication.get());
 
         if (mmi == null) {
             Rlog.e(LOG_TAG, "Mmi is NULL!");
             return false;
-        } else if (mmi.isPukCommand()) {
+        } else if (mmi.isPinPukCommand()) {
             mPendingMmis.add(mmi);
             mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
             mmi.processCode();
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java b/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
index 4c58709..07c137b 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 
 import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.uicc.UiccCardApplication;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 import com.android.internal.telephony.MmiCode;
 
 import android.os.AsyncResult;
@@ -43,8 +45,11 @@
     // From TS 22.030 6.5.2
     static final String ACTION_REGISTER = "**";
 
-    // Supp Service codes from TS 22.030 Annex B
+    // Supplementary Service codes for PIN/PIN2/PUK/PUK2 from TS 22.030 Annex B
+    static final String SC_PIN          = "04";
+    static final String SC_PIN2         = "042";
     static final String SC_PUK          = "05";
+    static final String SC_PUK2         = "052";
 
     // Event Constant
 
@@ -54,6 +59,7 @@
 
     CDMAPhone mPhone;
     Context mContext;
+    UiccCardApplication mUiccApplication;
 
     String mAction;              // ACTION_REGISTER
     String mSc;                  // Service Code
@@ -98,7 +104,7 @@
      */
 
     public static CdmaMmiCode
-    newFromDialString(String dialString, CDMAPhone phone) {
+    newFromDialString(String dialString, CDMAPhone phone, UiccCardApplication app) {
         Matcher m;
         CdmaMmiCode ret = null;
 
@@ -106,7 +112,7 @@
 
         // Is this formatted like a standard supplementary service code?
         if (m.matches()) {
-            ret = new CdmaMmiCode(phone);
+            ret = new CdmaMmiCode(phone,app);
             ret.mPoundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING));
             ret.mAction = makeEmptyNull(m.group(MATCH_GROUP_ACTION));
             ret.mSc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
@@ -135,10 +141,11 @@
 
     // Constructor
 
-    CdmaMmiCode (CDMAPhone phone) {
+    CdmaMmiCode (CDMAPhone phone, UiccCardApplication app) {
         super(phone.getHandler().getLooper());
         mPhone = phone;
         mContext = phone.getContext();
+        mUiccApplication = app;
     }
 
     // MmiCode implementation
@@ -178,9 +185,10 @@
     /**
      * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related
      */
-    boolean isPukCommand() {
-        return mSc != null && mSc.equals(SC_PUK);
-     }
+    boolean isPinPukCommand() {
+        return mSc != null && (mSc.equals(SC_PIN) || mSc.equals(SC_PIN2)
+                              || mSc.equals(SC_PUK) || mSc.equals(SC_PUK2));
+    }
 
     boolean isRegister() {
         return mAction != null && mAction.equals(ACTION_REGISTER);
@@ -194,31 +202,56 @@
 
     /** Process a MMI PUK code */
     void
-    processCode () {
+    processCode() {
         try {
-            if (isPukCommand()) {
-                // sia = old PUK
+            if (isPinPukCommand()) {
+                // TODO: This is the same as the code in GsmMmiCode.java,
+                // MmiCode should be an abstract or base class and this and
+                // other common variables and code should be promoted.
+
+                // sia = old PIN or PUK
                 // sib = new PIN
                 // sic = new PIN
                 String oldPinOrPuk = mSia;
-                String newPin = mSib;
-                int pinLen = newPin.length();
+                String newPinOrPuk = mSib;
+                int pinLen = newPinOrPuk.length();
                 if (isRegister()) {
-                    if (!newPin.equals(mSic)) {
+                    if (!newPinOrPuk.equals(mSic)) {
                         // password mismatch; return error
                         handlePasswordError(com.android.internal.R.string.mismatchPin);
                     } else if (pinLen < 4 || pinLen > 8 ) {
                         // invalid length
                         handlePasswordError(com.android.internal.R.string.invalidPin);
+                    } else if (mSc.equals(SC_PIN)
+                            && mUiccApplication != null
+                            && mUiccApplication.getState() == AppState.APPSTATE_PUK) {
+                        // Sim is puk-locked
+                        handlePasswordError(com.android.internal.R.string.needPuk);
+                    } else if (mUiccApplication != null) {
+                        Rlog.d(LOG_TAG, "process mmi service code using UiccApp sc=" + mSc);
+
+                        // We have an app and the pre-checks are OK
+                        if (mSc.equals(SC_PIN)) {
+                            mUiccApplication.changeIccLockPassword(oldPinOrPuk, newPinOrPuk,
+                                    obtainMessage(EVENT_SET_COMPLETE, this));
+                        } else if (mSc.equals(SC_PIN2)) {
+                            mUiccApplication.changeIccFdnPassword(oldPinOrPuk, newPinOrPuk,
+                                    obtainMessage(EVENT_SET_COMPLETE, this));
+                        } else if (mSc.equals(SC_PUK)) {
+                            mUiccApplication.supplyPuk(oldPinOrPuk, newPinOrPuk,
+                                    obtainMessage(EVENT_SET_COMPLETE, this));
+                        } else if (mSc.equals(SC_PUK2)) {
+                            mUiccApplication.supplyPuk2(oldPinOrPuk, newPinOrPuk,
+                                    obtainMessage(EVENT_SET_COMPLETE, this));
+                        } else {
+                            throw new RuntimeException("Unsupported service code=" + mSc);
+                        }
                     } else {
-                        mPhone.mCi.supplyIccPuk(oldPinOrPuk, newPin,
-                                obtainMessage(EVENT_SET_COMPLETE, this));
+                        throw new RuntimeException("No application mUiccApplicaiton is null");
                     }
                 } else {
-                    throw new RuntimeException ("Invalid or Unsupported MMI Code");
+                    throw new RuntimeException ("Ivalid register/action=" + mAction);
                 }
-            } else {
-                throw new RuntimeException ("Invalid or Unsupported MMI Code");
             }
         } catch (RuntimeException exc) {
             mState = State.FAILED;
@@ -243,7 +276,7 @@
 
         if (msg.what == EVENT_SET_COMPLETE) {
             ar = (AsyncResult) (msg.obj);
-            onSetComplete(ar);
+            onSetComplete(msg, ar);
         } else {
             Rlog.e(LOG_TAG, "Unexpected reply");
         }
@@ -252,7 +285,7 @@
 
     private CharSequence getScString() {
         if (mSc != null) {
-            if (isPukCommand()) {
+            if (isPinPukCommand()) {
                 return mContext.getText(com.android.internal.R.string.PinMmi);
             }
         }
@@ -261,7 +294,7 @@
     }
 
     private void
-    onSetComplete(AsyncResult ar){
+    onSetComplete(Message msg, AsyncResult ar){
         StringBuilder sb = new StringBuilder(getScString());
         sb.append("\n");
 
@@ -270,13 +303,38 @@
             if (ar.exception instanceof CommandException) {
                 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError();
                 if (err == CommandException.Error.PASSWORD_INCORRECT) {
-                    if (isPukCommand()) {
-                        sb.append(mContext.getText(
-                                com.android.internal.R.string.badPuk));
+                    if (isPinPukCommand()) {
+                        // look specifically for the PUK commands and adjust
+                        // the message accordingly.
+                        if (mSc.equals(SC_PUK) || mSc.equals(SC_PUK2)) {
+                            sb.append(mContext.getText(
+                                    com.android.internal.R.string.badPuk));
+                        } else {
+                            sb.append(mContext.getText(
+                                    com.android.internal.R.string.badPin));
+                        }
+                        // Get the No. of retries remaining to unlock PUK/PUK2
+                        int attemptsRemaining = msg.arg1;
+                        if (attemptsRemaining >= 0) {
+                            Rlog.d(LOG_TAG, "onSetComplete: attemptsRemaining="+attemptsRemaining);
+                            sb.append(mContext.getResources().getQuantityString(
+                                    com.android.internal.R.plurals.pinpuk_attempts,
+                                    attemptsRemaining, attemptsRemaining));
+                        }
                     } else {
                         sb.append(mContext.getText(
                                 com.android.internal.R.string.passwordIncorrect));
                     }
+                } else if (err == CommandException.Error.SIM_PUK2) {
+                    sb.append(mContext.getText(
+                            com.android.internal.R.string.badPin));
+                    sb.append("\n");
+                    sb.append(mContext.getText(
+                            com.android.internal.R.string.needPuk2));
+                } else if (err == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+                    if (mSc.equals(SC_PIN)) {
+                        sb.append(mContext.getText(com.android.internal.R.string.enablePin));
+                    }
                 } else {
                     sb.append(mContext.getText(
                             com.android.internal.R.string.mmiError));
diff --git a/src/java/com/android/internal/telephony/gsm/GSMPhone.java b/src/java/com/android/internal/telephony/gsm/GSMPhone.java
index a7991ef..f80d822 100644
--- a/src/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/src/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -753,7 +753,7 @@
     public boolean handlePinMmi(String dialString) {
         GsmMmiCode mmi = GsmMmiCode.newFromDialString(dialString, this, mUiccApplication.get());
 
-        if (mmi != null && mmi.isPinCommand()) {
+        if (mmi != null && mmi.isPinPukCommand()) {
             mPendingMMIs.add(mmi);
             mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
             mmi.processCode();
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index f41391d..1dd958f 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -564,7 +564,7 @@
     /**
      * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related
      */
-    boolean isPinCommand() {
+    boolean isPinPukCommand() {
         return mSc != null && (mSc.equals(SC_PIN) || mSc.equals(SC_PIN2)
                               || mSc.equals(SC_PUK) || mSc.equals(SC_PUK2));
      }
@@ -779,43 +779,53 @@
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
                 }
-            } else if (isPinCommand()) {
+            } else if (isPinPukCommand()) {
+                // TODO: This is the same as the code in CmdaMmiCode.java,
+                // MmiCode should be an abstract or base class and this and
+                // other common variables and code should be promoted.
+
                 // sia = old PIN or PUK
                 // sib = new PIN
                 // sic = new PIN
                 String oldPinOrPuk = mSia;
-                String newPin = mSib;
-                int pinLen = newPin.length();
+                String newPinOrPuk = mSib;
+                int pinLen = newPinOrPuk.length();
                 if (isRegister()) {
-                    if (!newPin.equals(mSic)) {
+                    if (!newPinOrPuk.equals(mSic)) {
                         // password mismatch; return error
                         handlePasswordError(com.android.internal.R.string.mismatchPin);
                     } else if (pinLen < 4 || pinLen > 8 ) {
                         // invalid length
                         handlePasswordError(com.android.internal.R.string.invalidPin);
-                    } else if (mSc.equals(SC_PIN) &&
-                               mUiccApplication != null &&
-                               mUiccApplication.getState() == AppState.APPSTATE_PUK ) {
+                    } else if (mSc.equals(SC_PIN)
+                            && mUiccApplication != null
+                            && mUiccApplication.getState() == AppState.APPSTATE_PUK) {
                         // Sim is puk-locked
                         handlePasswordError(com.android.internal.R.string.needPuk);
-                    } else {
-                        // pre-checks OK
+                    } else if (mUiccApplication != null) {
+                        Rlog.d(LOG_TAG, "process mmi service code using UiccApp sc=" + mSc);
+
+                        // We have an app and the pre-checks are OK
                         if (mSc.equals(SC_PIN)) {
-                            mPhone.mCi.changeIccPin(oldPinOrPuk, newPin,
+                            mUiccApplication.changeIccLockPassword(oldPinOrPuk, newPinOrPuk,
                                     obtainMessage(EVENT_SET_COMPLETE, this));
                         } else if (mSc.equals(SC_PIN2)) {
-                            mPhone.mCi.changeIccPin2(oldPinOrPuk, newPin,
+                            mUiccApplication.changeIccFdnPassword(oldPinOrPuk, newPinOrPuk,
                                     obtainMessage(EVENT_SET_COMPLETE, this));
                         } else if (mSc.equals(SC_PUK)) {
-                            mPhone.mCi.supplyIccPuk(oldPinOrPuk, newPin,
+                            mUiccApplication.supplyPuk(oldPinOrPuk, newPinOrPuk,
                                     obtainMessage(EVENT_SET_COMPLETE, this));
                         } else if (mSc.equals(SC_PUK2)) {
-                            mPhone.mCi.supplyIccPuk2(oldPinOrPuk, newPin,
+                            mUiccApplication.supplyPuk2(oldPinOrPuk, newPinOrPuk,
                                     obtainMessage(EVENT_SET_COMPLETE, this));
+                        } else {
+                            throw new RuntimeException("uicc unsupported service code=" + mSc);
                         }
+                    } else {
+                        throw new RuntimeException("No application mUiccApplicaiton is null");
                     }
                 } else {
-                    throw new RuntimeException ("Invalid or Unsupported MMI Code");
+                    throw new RuntimeException ("Ivalid register/action=" + mAction);
                 }
             } else if (mPoundString != null) {
                 sendUssd(mPoundString);
@@ -904,7 +914,7 @@
             case EVENT_SET_COMPLETE:
                 ar = (AsyncResult) (msg.obj);
 
-                onSetComplete(ar);
+                onSetComplete(msg, ar);
                 break;
 
             case EVENT_SET_CFF_COMPLETE:
@@ -921,7 +931,7 @@
                     }
                 }
 
-                onSetComplete(ar);
+                onSetComplete(msg, ar);
                 break;
 
             case EVENT_GET_CLIR_COMPLETE:
@@ -990,7 +1000,7 @@
                 return mContext.getText(com.android.internal.R.string.PwdMmi);
             } else if (mSc.equals(SC_WAIT)) {
                 return mContext.getText(com.android.internal.R.string.CwMmi);
-            } else if (isPinCommand()) {
+            } else if (isPinPukCommand()) {
                 return mContext.getText(com.android.internal.R.string.PinMmi);
             }
         }
@@ -999,7 +1009,7 @@
     }
 
     private void
-    onSetComplete(AsyncResult ar){
+    onSetComplete(Message msg, AsyncResult ar){
         StringBuilder sb = new StringBuilder(getScString());
         sb.append("\n");
 
@@ -1008,7 +1018,7 @@
             if (ar.exception instanceof CommandException) {
                 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError();
                 if (err == CommandException.Error.PASSWORD_INCORRECT) {
-                    if (isPinCommand()) {
+                    if (isPinPukCommand()) {
                         // look specifically for the PUK commands and adjust
                         // the message accordingly.
                         if (mSc.equals(SC_PUK) || mSc.equals(SC_PUK2)) {
@@ -1018,6 +1028,13 @@
                             sb.append(mContext.getText(
                                     com.android.internal.R.string.badPin));
                         }
+                        // Get the No. of retries remaining to unlock PUK/PUK2
+                        int attemptsRemaining = msg.arg1;
+                        if (attemptsRemaining >= 0) {
+                            sb.append(mContext.getResources().getQuantityString(
+                                    com.android.internal.R.plurals.pinpuk_attempts,
+                                    attemptsRemaining, attemptsRemaining));
+                        }
                     } else {
                         sb.append(mContext.getText(
                                 com.android.internal.R.string.passwordIncorrect));
@@ -1028,6 +1045,10 @@
                     sb.append("\n");
                     sb.append(mContext.getText(
                             com.android.internal.R.string.needPuk2));
+                } else if (err == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+                    if (mSc.equals(SC_PIN)) {
+                        sb.append(mContext.getText(com.android.internal.R.string.enablePin));
+                    }
                 } else if (err == CommandException.Error.FDN_CHECK_FAILURE) {
                     Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE");
                     sb.append(mContext.getText(com.android.internal.R.string.mmiFdnError));
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
index 733af64..c512ecf 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
@@ -604,8 +604,9 @@
     @Override
     public boolean getIccLockEnabled() {
         synchronized (mLock) {
-            /* defaults to true, if ICC is absent */
-            Boolean retValue = mUiccApplication != null ? mUiccApplication.getIccLockEnabled() : true;
+            /* defaults to false, if ICC is absent/deactivated */
+            Boolean retValue = mUiccApplication != null ?
+                    mUiccApplication.getIccLockEnabled() : false;
             return retValue;
         }
     }
@@ -613,11 +614,29 @@
     @Override
     public boolean getIccFdnEnabled() {
         synchronized (mLock) {
-            Boolean retValue = mUiccApplication != null ? mUiccApplication.getIccFdnEnabled() : false;
+            Boolean retValue = mUiccApplication != null ?
+                    mUiccApplication.getIccFdnEnabled() : false;
             return retValue;
         }
     }
 
+    public boolean getIccFdnAvailable() {
+        boolean retValue = mUiccApplication != null ? mUiccApplication.getIccFdnAvailable() : false;
+        return retValue;
+    }
+
+    public boolean getIccPin2Blocked() {
+        /* defaults to disabled */
+        Boolean retValue = mUiccApplication != null ? mUiccApplication.getIccPin2Blocked() : false;
+        return retValue;
+    }
+
+    public boolean getIccPuk2Blocked() {
+        /* defaults to disabled */
+        Boolean retValue = mUiccApplication != null ? mUiccApplication.getIccPuk2Blocked() : false;
+        return retValue;
+    }
+
     @Override
     public void setIccLockEnabled(boolean enabled, String password, Message onComplete) {
         synchronized (mLock) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
index 0715b0e..d125dbe 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -40,10 +40,14 @@
     private static final String LOG_TAG = "UiccCardApplication";
     private static final boolean DBG = true;
 
-    private static final int EVENT_QUERY_FACILITY_FDN_DONE = 1;
-    private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 2;
-    private static final int EVENT_QUERY_FACILITY_LOCK_DONE = 3;
-    private static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 4;
+    private static final int EVENT_PIN1_PUK1_DONE = 1;
+    private static final int EVENT_CHANGE_PIN1_DONE = 2;
+    private static final int EVENT_CHANGE_PIN2_DONE = 3;
+    private static final int EVENT_QUERY_FACILITY_FDN_DONE = 4;
+    private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 5;
+    private static final int EVENT_QUERY_FACILITY_LOCK_DONE = 6;
+    private static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 7;
+    private static final int EVENT_PIN2_PUK2_DONE = 8;
 
     private final Object  mLock = new Object();
     private UiccCard      mUiccCard; //parent
@@ -59,6 +63,7 @@
     private boolean       mDesiredFdnEnabled;
     private boolean       mIccLockEnabled;
     private boolean       mDesiredPinLocked;
+    private boolean       mIccFdnAvailable = true; // Default is enabled.
 
     private CommandsInterface mCi;
     private Context mContext;
@@ -209,10 +214,18 @@
                 return;
             }
 
-            int[] ints = (int[])ar.result;
-            if(ints.length != 0) {
-                mIccFdnEnabled = (0!=ints[0]);
-                if (DBG) log("Query facility lock : "  + mIccFdnEnabled);
+            int[] result = (int[])ar.result;
+            if(result.length != 0) {
+                //0 - Available & Disabled, 1-Available & Enabled, 2-Unavailable.
+                if (result[0] == 2) {
+                    mIccFdnEnabled = false;
+                    mIccFdnAvailable = false;
+                } else {
+                    mIccFdnEnabled = (result[0] == 1) ? true : false;
+                    mIccFdnAvailable = true;
+                }
+                log("Query facility FDN : FDN service available: "+ mIccFdnAvailable
+                        +" enabled: "  + mIccFdnEnabled);
             } else {
                 loge("Bogus facility lock response");
             }
@@ -221,14 +234,18 @@
 
     private void onChangeFdnDone(AsyncResult ar) {
         synchronized (mLock) {
+            int attemptsRemaining = -1;
+
             if (ar.exception == null) {
                 mIccFdnEnabled = mDesiredFdnEnabled;
                 if (DBG) log("EVENT_CHANGE_FACILITY_FDN_DONE: " +
                         "mIccFdnEnabled=" + mIccFdnEnabled);
             } else {
+                attemptsRemaining = parsePinPukErrorResult(ar);
                 loge("Error change facility fdn with exception " + ar.exception);
             }
             Message response = (Message)ar.userObj;
+            response.arg1 = attemptsRemaining;
             AsyncResult.forMessage(response).exception = ar.exception;
             response.sendToTarget();
         }
@@ -297,15 +314,38 @@
     /** REMOVE when mIccLockEnabled is not needed */
     private void onChangeFacilityLock(AsyncResult ar) {
         synchronized (mLock) {
+            int attemptsRemaining = -1;
+
             if (ar.exception == null) {
                 mIccLockEnabled = mDesiredPinLocked;
                 if (DBG) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: mIccLockEnabled= "
                         + mIccLockEnabled);
             } else {
+                attemptsRemaining = parsePinPukErrorResult(ar);
                 loge("Error change facility lock with exception " + ar.exception);
             }
-            AsyncResult.forMessage(((Message)ar.userObj)).exception = ar.exception;
-            ((Message)ar.userObj).sendToTarget();
+            Message response = (Message)ar.userObj;
+            AsyncResult.forMessage(response).exception = ar.exception;
+            response.arg1 = attemptsRemaining;
+            response.sendToTarget();
+        }
+    }
+
+    /**
+     * Parse the error response to obtain number of attempts remaining
+     */
+    private int parsePinPukErrorResult(AsyncResult ar) {
+        int[] result = (int[]) ar.result;
+        if (result == null) {
+            return -1;
+        } else {
+            int length = result.length;
+            int attemptsRemaining = -1;
+            if (length > 0) {
+                attemptsRemaining = result[0];
+            }
+            log("parsePinPukErrorResult: attemptsRemaining=" + attemptsRemaining);
+            return attemptsRemaining;
         }
     }
 
@@ -321,6 +361,22 @@
             }
 
             switch (msg.what) {
+                case EVENT_PIN1_PUK1_DONE:
+                case EVENT_PIN2_PUK2_DONE:
+                case EVENT_CHANGE_PIN1_DONE:
+                case EVENT_CHANGE_PIN2_DONE:
+                    // a PIN/PUK/PIN2/PUK2 complete
+                    // request has completed. ar.userObj is the response Message
+                    int attemptsRemaining = -1;
+                    ar = (AsyncResult)msg.obj;
+                    if ((ar.exception != null) && (ar.result != null)) {
+                        attemptsRemaining = parsePinPukErrorResult(ar);
+                    }
+                    Message response = (Message)ar.userObj;
+                    AsyncResult.forMessage(response).exception = ar.exception;
+                    response.arg1 = attemptsRemaining;
+                    response.sendToTarget();
+                    break;
                 case EVENT_QUERY_FACILITY_FDN_DONE:
                     ar = (AsyncResult)msg.obj;
                     onQueryFdnEnabled(ar);
@@ -520,6 +576,34 @@
      * Handler.
      *
      * onComplete.obj will be an AsyncResult
+     * onComplete.arg1 = remaining attempts before puk locked or -1 if unknown
+     *
+     * ((AsyncResult)onComplete.obj).exception == null on success
+     * ((AsyncResult)onComplete.obj).exception != null on fail
+     *
+     * If the supplied PIN is incorrect:
+     * ((AsyncResult)onComplete.obj).exception != null
+     * && ((AsyncResult)onComplete.obj).exception
+     *       instanceof com.android.internal.telephony.gsm.CommandException)
+     * && ((CommandException)(((AsyncResult)onComplete.obj).exception))
+     *          .getCommandError() == CommandException.Error.PASSWORD_INCORRECT
+     */
+    public void supplyPin (String pin, Message onComplete) {
+        synchronized (mLock) {
+            mCi.supplyIccPinForApp(pin, mAid, mHandler.obtainMessage(EVENT_PIN1_PUK1_DONE,
+                    onComplete));
+        }
+    }
+
+    /**
+     * Supply the ICC PUK to the ICC
+     *
+     * When the operation is complete, onComplete will be sent to its
+     * Handler.
+     *
+     * onComplete.obj will be an AsyncResult
+     * onComplete.arg1 = remaining attempts before Icc will be permanently unusable
+     * or -1 if unknown
      *
      * ((AsyncResult)onComplete.obj).exception == null on success
      * ((AsyncResult)onComplete.obj).exception != null on fail
@@ -533,27 +617,24 @@
      *
      *
      */
-    public void supplyPin (String pin, Message onComplete) {
-        synchronized (mLock) {
-            mCi.supplyIccPin(pin, onComplete);
-        }
-    }
-
     public void supplyPuk (String puk, String newPin, Message onComplete) {
         synchronized (mLock) {
-            mCi.supplyIccPuk(puk, newPin, onComplete);
+        mCi.supplyIccPukForApp(puk, newPin, mAid,
+                mHandler.obtainMessage(EVENT_PIN1_PUK1_DONE, onComplete));
         }
     }
 
     public void supplyPin2 (String pin2, Message onComplete) {
         synchronized (mLock) {
-            mCi.supplyIccPin2(pin2, onComplete);
+            mCi.supplyIccPin2ForApp(pin2, mAid,
+                    mHandler.obtainMessage(EVENT_PIN2_PUK2_DONE, onComplete));
         }
     }
 
     public void supplyPuk2 (String puk2, String newPin2, Message onComplete) {
         synchronized (mLock) {
-            mCi.supplyIccPuk2(puk2, newPin2, onComplete);
+            mCi.supplyIccPuk2ForApp(puk2, newPin2, mAid,
+                    mHandler.obtainMessage(EVENT_PIN2_PUK2_DONE, onComplete));
         }
     }
 
@@ -597,6 +678,15 @@
     }
 
     /**
+     * Check whether fdn (fixed dialing number) service is available.
+     * @return true if ICC fdn service available
+     *         false if ICC fdn service not available
+     */
+    public boolean getIccFdnAvailable() {
+        return mIccFdnAvailable;
+    }
+
+    /**
      * Set the ICC pin lock enabled or disabled
      * When the operation is complete, onComplete will be sent to its handler
      *
@@ -659,6 +749,7 @@
      * @param newPassword is the new password
      * @param onComplete
      *        onComplete.obj will be an AsyncResult
+     *        onComplete.arg1 = attempts remaining or -1 if unknown
      *        ((AsyncResult)onComplete.obj).exception == null on success
      *        ((AsyncResult)onComplete.obj).exception != null on fail
      */
@@ -667,7 +758,7 @@
         synchronized (mLock) {
             if (DBG) log("changeIccLockPassword");
             mCi.changeIccPinForApp(oldPassword, newPassword, mAid,
-                    onComplete);
+                    mHandler.obtainMessage(EVENT_CHANGE_PIN1_DONE, onComplete));
         }
     }
 
@@ -687,7 +778,25 @@
         synchronized (mLock) {
             if (DBG) log("changeIccFdnPassword");
             mCi.changeIccPin2ForApp(oldPassword, newPassword, mAid,
-                    onComplete);
+                    mHandler.obtainMessage(EVENT_CHANGE_PIN2_DONE, onComplete));
+        }
+    }
+
+    /**
+     * @return true if ICC card is PIN2 blocked
+     */
+    public boolean getIccPin2Blocked() {
+        synchronized (mLock) {
+            return mPin2State == PinState.PINSTATE_ENABLED_BLOCKED;
+        }
+    }
+
+    /**
+     * @return true if ICC card is PUK2 blocked
+     */
+    public boolean getIccPuk2Blocked() {
+        synchronized (mLock) {
+            return mPin2State == PinState.PINSTATE_ENABLED_PERM_BLOCKED;
         }
     }