content: /*
   * Copyright (C) 2006, 2012 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.internal.telephony.uicc;
  
  import android.annotation.UnsupportedAppUsage;
  import android.content.Context;
  import android.os.AsyncResult;
  import android.os.Handler;
  import android.os.Message;
  import android.os.Registrant;
  import android.os.RegistrantList;
  import android.telephony.Rlog;
  
  import com.android.internal.telephony.CommandsInterface;
  import com.android.internal.telephony.PhoneConstants;
  import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
  import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
  import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
  import com.android.internal.telephony.uicc.IccCardStatus.PinState;
  
  import java.io.FileDescriptor;
  import java.io.PrintWriter;
  
  /**
   * {@hide}
   */
  public class UiccCardApplication {
      private static final String LOG_TAG = "UiccCardApplication";
      private static final boolean DBG = true;
  
      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 static final int EVENT_RADIO_UNAVAILABLE = 9;
  
      /**
       * These values are for authContext (parameter P2) per 3GPP TS 31.102 (Section 7.1.2)
       */
      public static final int AUTH_CONTEXT_EAP_SIM = PhoneConstants.AUTH_CONTEXT_EAP_SIM;
      public static final int AUTH_CONTEXT_EAP_AKA = PhoneConstants.AUTH_CONTEXT_EAP_AKA;
      public static final int AUTH_CONTEXT_UNDEFINED = PhoneConstants.AUTH_CONTEXT_UNDEFINED;
  
      @UnsupportedAppUsage
      private final Object  mLock = new Object();
      private UiccProfile   mUiccProfile; //parent
      @UnsupportedAppUsage
      private AppState      mAppState;
      @UnsupportedAppUsage
      private AppType       mAppType;
      private int           mAuthContext;
      @UnsupportedAppUsage
      private PersoSubState mPersoSubState;
      @UnsupportedAppUsage
      private String        mAid;
      private String        mAppLabel;
      private boolean       mPin1Replaced;
      @UnsupportedAppUsage
      private PinState      mPin1State;
      private PinState      mPin2State;
      private boolean       mIccFdnEnabled;
      private boolean       mDesiredFdnEnabled;
      private boolean       mIccLockEnabled;
      private boolean       mDesiredPinLocked;
  
      // App state will be ignored while deciding whether the card is ready or not.
      private boolean       mIgnoreApp;
      private boolean       mIccFdnAvailable = true; // Default is enabled.
  
      @UnsupportedAppUsage
      private CommandsInterface mCi;
      private Context mContext;
      private IccRecords mIccRecords;
      private IccFileHandler mIccFh;
  
      @UnsupportedAppUsage
      private boolean mDestroyed;//set to true once this App is commanded to be disposed of.
  
      private RegistrantList mReadyRegistrants = new RegistrantList();
      private RegistrantList mPinLockedRegistrants = new RegistrantList();
      private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
  
      public UiccCardApplication(UiccProfile uiccProfile,
                          IccCardApplicationStatus as,
                          Context c,
                          CommandsInterface ci) {
          if (DBG) log("Creating UiccApp: " + as);
          mUiccProfile = uiccProfile;
          mAppState = as.app_state;
          mAppType = as.app_type;
          mAuthContext = getAuthContext(mAppType);
          mPersoSubState = as.perso_substate;
          mAid = as.aid;
          mAppLabel = as.app_label;
          mPin1Replaced = (as.pin1_replaced != 0);
          mPin1State = as.pin1;
          mPin2State = as.pin2;
          mIgnoreApp = false;
  
          mContext = c;
          mCi = ci;
  
          mIccFh = createIccFileHandler(as.app_type);
          mIccRecords = createIccRecords(as.app_type, mContext, mCi);
          if (mAppState == AppState.APPSTATE_READY) {
              queryFdn();
              queryPin1State();
          }
          mCi.registerForNotAvailable(mHandler, EVENT_RADIO_UNAVAILABLE, null);
      }
  
      @UnsupportedAppUsage
      public void update (IccCardApplicationStatus as, Context c, CommandsInterface ci) {
          synchronized (mLock) {
              if (mDestroyed) {
                  loge("Application updated after destroyed! Fix me!");
                  return;
              }
  
              if (DBG) log(mAppType + " update. New " + as);
              mContext = c;
              mCi = ci;
              AppType oldAppType = mAppType;
              AppState oldAppState = mAppState;
              PersoSubState oldPersoSubState = mPersoSubState;
              mAppType = as.app_type;
              mAuthContext = getAuthContext(mAppType);
              mAppState = as.app_state;
              mPersoSubState = as.perso_substate;
              mAid = as.aid;
              mAppLabel = as.app_label;
              mPin1Replaced = (as.pin1_replaced != 0);
              mPin1State = as.pin1;
              mPin2State = as.pin2;
  
              if (mAppType != oldAppType) {
                  if (mIccFh != null) { mIccFh.dispose();}
                  if (mIccRecords != null) { mIccRecords.dispose();}
                  mIccFh = createIccFileHandler(as.app_type);
                  mIccRecords = createIccRecords(as.app_type, c, ci);
              }
  
              if (mPersoSubState != oldPersoSubState &&
                      mPersoSubState == PersoSubState.PERSOSUBSTATE_SIM_NETWORK) {
                  notifyNetworkLockedRegistrantsIfNeeded(null);
              }
  
              if (mAppState != oldAppState) {
                  if (DBG) log(oldAppType + " changed state: " + oldAppState + " -> " + mAppState);
                  // If the app state turns to APPSTATE_READY, then query FDN status,
                  //as it might have failed in earlier attempt.
                  if (mAppState == AppState.APPSTATE_READY) {
                      queryFdn();
                      queryPin1State();
                  }
                  notifyPinLockedRegistrantsIfNeeded(null);
                  notifyReadyRegistrantsIfNeeded(null);
              }
          }
      }
  
      @UnsupportedAppUsage
      void dispose() {
          synchronized (mLock) {
              if (DBG) log(mAppType + " being Disposed");
              mDestroyed = true;
              if (mIccRecords != null) { mIccRecords.dispose();}
              if (mIccFh != null) { mIccFh.dispose();}
              mIccRecords = null;
              mIccFh = null;
              mCi.unregisterForNotAvailable(mHandler);
          }
      }
  
      private IccRecords createIccRecords(AppType type, Context c, CommandsInterface ci) {
          if (type == AppType.APPTYPE_USIM || type == AppType.APPTYPE_SIM) {
              return new SIMRecords(this, c, ci);
          } else if (type == AppType.APPTYPE_RUIM || type == AppType.APPTYPE_CSIM){
              return new RuimRecords(this, c, ci);
          } else if (type == AppType.APPTYPE_ISIM) {
              return new IsimUiccRecords(this, c, ci);
          } else {
              // Unknown app type (maybe detection is still in progress)
              return null;
          }
      }
  
      private IccFileHandler createIccFileHandler(AppType type) {
          switch (type) {
              case APPTYPE_SIM:
                  return new SIMFileHandler(this, mAid, mCi);
              case APPTYPE_RUIM:
                  return new RuimFileHandler(this, mAid, mCi);
              case APPTYPE_USIM:
                  return new UsimFileHandler(this, mAid, mCi);
              case APPTYPE_CSIM:
                  return new CsimFileHandler(this, mAid, mCi);
              case APPTYPE_ISIM:
                  return new IsimFileHandler(this, mAid, mCi);
              default:
                  return null;
          }
      }
  
      /** Assumes mLock is held. */
      public void queryFdn() {
          //This shouldn't change run-time. So needs to be called only once.
          int serviceClassX;
  
          serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
                          CommandsInterface.SERVICE_CLASS_DATA +
                          CommandsInterface.SERVICE_CLASS_FAX;
          mCi.queryFacilityLockForApp (
                  CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX,
                  mAid, mHandler.obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE));
      }
      /**
       * Interpret EVENT_QUERY_FACILITY_LOCK_DONE
       * @param ar is asyncResult of Query_Facility_Locked
       */
      private void onQueryFdnEnabled(AsyncResult ar) {
          synchronized (mLock) {
              if (ar.exception != null) {
                  if (DBG) log("Error in querying facility lock:" + ar.exception);
                  return;
              }
  
              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");
              }
          }
      }
  
      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();
          }
      }
  
      /** REMOVE when mIccLockEnabled is not needed, assumes mLock is held */
      private void queryPin1State() {
          int serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
                  CommandsInterface.SERVICE_CLASS_DATA +
                  CommandsInterface.SERVICE_CLASS_FAX;
          mCi.queryFacilityLockForApp (
              CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
              mAid, mHandler.obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
      }
  
      /** REMOVE when mIccLockEnabled is not needed*/
      private void onQueryFacilityLock(AsyncResult ar) {
          synchronized (mLock) {
              if(ar.exception != null) {
                  if (DBG) log("Error in querying facility lock:" + ar.exception);
                  return;
              }
  
              int[] ints = (int[])ar.result;
              if(ints.length != 0) {
                  if (DBG) log("Query facility lock : "  + ints[0]);
  
                  mIccLockEnabled = (ints[0] != 0);
  
                  // Sanity check: we expect mPin1State to match mIccLockEnabled.
                  // When mPin1State is DISABLED mIccLockEanbled should be false.
                  // When mPin1State is ENABLED mIccLockEnabled should be true.
                  //
                  // Here we validate these assumptions to assist in identifying which ril/radio's
                  // have not correctly implemented GET_SIM_STATUS
                  switch (mPin1State) {
                      case PINSTATE_DISABLED:
                          if (mIccLockEnabled) {
                              loge("QUERY_FACILITY_LOCK:enabled GET_SIM_STATUS.Pin1:disabled."
                                      + " Fixme");
                          }
                          break;
                      case PINSTATE_ENABLED_NOT_VERIFIED:
                      case PINSTATE_ENABLED_VERIFIED:
                      case PINSTATE_ENABLED_BLOCKED:
                      case PINSTATE_ENABLED_PERM_BLOCKED:
                          if (!mIccLockEnabled) {
                              loge("QUERY_FACILITY_LOCK:disabled GET_SIM_STATUS.Pin1:enabled."
                                      + " Fixme");
                          }
                      case PINSTATE_UNKNOWN:
                      default:
                          if (DBG) log("Ignoring: pin1state=" + mPin1State);
                          break;
                  }
              } else {
                  loge("Bogus facility lock response");
              }
          }
      }
  
      /** 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);
              }
              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;
          }
      }
  
      private Handler mHandler = new Handler() {
          @Override
          public void handleMessage(Message msg){
              AsyncResult ar;
  
              if (mDestroyed) {
                  loge("Received message " + msg + "[" + msg.what
                          + "] while being destroyed. Ignoring.");
                  return;
              }
  
              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
                      ar = (AsyncResult)msg.obj;
                      int 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);
                      break;
                  case EVENT_CHANGE_FACILITY_FDN_DONE:
                      ar = (AsyncResult)msg.obj;
                      onChangeFdnDone(ar);
                      break;
                  case EVENT_QUERY_FACILITY_LOCK_DONE:
                      ar = (AsyncResult)msg.obj;
                      onQueryFacilityLock(ar);
                      break;
                  case EVENT_CHANGE_FACILITY_LOCK_DONE:
                      ar = (AsyncResult)msg.obj;
                      onChangeFacilityLock(ar);
                      break;
                  case EVENT_RADIO_UNAVAILABLE:
                      if (DBG) log("handleMessage (EVENT_RADIO_UNAVAILABLE)");
                      mAppState = AppState.APPSTATE_UNKNOWN;
                      break;
                  default:
                      loge("Unknown Event " + msg.what);
              }
          }
      };
  
      @UnsupportedAppUsage
      public void registerForReady(Handler h, int what, Object obj) {
          synchronized (mLock) {
              Registrant r = new Registrant (h, what, obj);
              mReadyRegistrants.add(r);
              notifyReadyRegistrantsIfNeeded(r);
          }
      }
  
      @UnsupportedAppUsage
      public void unregisterForReady(Handler h) {
          synchronized (mLock) {
              mReadyRegistrants.remove(h);
          }
      }
  
      /**
       * Notifies handler of any transition into State.isPinLocked()
       */
      protected void registerForLocked(Handler h, int what, Object obj) {
          synchronized (mLock) {
              Registrant r = new Registrant (h, what, obj);
              mPinLockedRegistrants.add(r);
              notifyPinLockedRegistrantsIfNeeded(r);
          }
      }
  
      protected void unregisterForLocked(Handler h) {
          synchronized (mLock) {
              mPinLockedRegistrants.remove(h);
          }
      }
  
      /**
       * Notifies handler of any transition into State.NETWORK_LOCKED
       */
      protected void registerForNetworkLocked(Handler h, int what, Object obj) {
          synchronized (mLock) {
              Registrant r = new Registrant (h, what, obj);
              mNetworkLockedRegistrants.add(r);
              notifyNetworkLockedRegistrantsIfNeeded(r);
          }
      }
  
      protected void unregisterForNetworkLocked(Handler h) {
          synchronized (mLock) {
              mNetworkLockedRegistrants.remove(h);
          }
      }
  
      /**
       * Notifies specified registrant, assume mLock is held.
       *
       * @param r Registrant to be notified. If null - all registrants will be notified
       */
      private void notifyReadyRegistrantsIfNeeded(Registrant r) {
          if (mDestroyed) {
              return;
          }
          if (mAppState == AppState.APPSTATE_READY) {
              if (mPin1State == PinState.PINSTATE_ENABLED_NOT_VERIFIED ||
                      mPin1State == PinState.PINSTATE_ENABLED_BLOCKED ||
                      mPin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) {
                  loge("Sanity check failed! APPSTATE is ready while PIN1 is not verified!!!");
                  // Don't notify if application is in insane state
                  return;
              }
              if (r == null) {
                  if (DBG) log("Notifying registrants: READY");
                  mReadyRegistrants.notifyRegistrants();
              } else {
                  if (DBG) log("Notifying 1 registrant: READY");
                  r.notifyRegistrant(new AsyncResult(null, null, null));
              }
          }
      }
  
      /**
       * Notifies specified registrant, assume mLock is held.
       *
       * @param r Registrant to be notified. If null - all registrants will be notified
       */
      private void notifyPinLockedRegistrantsIfNeeded(Registrant r) {
          if (mDestroyed) {
              return;
          }
  
          if (mAppState == AppState.APPSTATE_PIN ||
                  mAppState == AppState.APPSTATE_PUK) {
              if (mPin1State == PinState.PINSTATE_ENABLED_VERIFIED ||
                      mPin1State == PinState.PINSTATE_DISABLED) {
                  loge("Sanity check failed! APPSTATE is locked while PIN1 is not!!!");
                  //Don't notify if application is in insane state
                  return;
              }
              if (r == null) {
                  if (DBG) log("Notifying registrants: LOCKED");
                  mPinLockedRegistrants.notifyRegistrants();
              } else {
                  if (DBG) log("Notifying 1 registrant: LOCKED");
                  r.notifyRegistrant(new AsyncResult(null, null, null));
              }
          }
      }
  
      /**
       * Notifies specified registrant, assume mLock is held.
       *
       * @param r Registrant to be notified. If null - all registrants will be notified
       */
      private void notifyNetworkLockedRegistrantsIfNeeded(Registrant r) {
          if (mDestroyed) {
              return;
          }
  
          if (mAppState == AppState.APPSTATE_SUBSCRIPTION_PERSO &&
                  mPersoSubState == PersoSubState.PERSOSUBSTATE_SIM_NETWORK) {
              if (r == null) {
                  if (DBG) log("Notifying registrants: NETWORK_LOCKED");
                  mNetworkLockedRegistrants.notifyRegistrants();
              } else {
                  if (DBG) log("Notifying 1 registrant: NETWORK_LOCED");
                  r.notifyRegistrant(new AsyncResult(null, null, null));
              }
          }
      }
  
      @UnsupportedAppUsage
      public AppState getState() {
          synchronized (mLock) {
              return mAppState;
          }
      }
  
      @UnsupportedAppUsage
      public AppType getType() {
          synchronized (mLock) {
              return mAppType;
          }
      }
  
      @UnsupportedAppUsage
      public int getAuthContext() {
          synchronized (mLock) {
              return mAuthContext;
          }
      }
  
      /**
       * Returns the authContext based on the type of UiccCard.
       *
       * @param appType the app type
       * @return authContext corresponding to the type or AUTH_CONTEXT_UNDEFINED if appType not
       * supported
       */
      private static int getAuthContext(AppType appType) {
          int authContext;
  
          switch (appType) {
              case APPTYPE_SIM:
                  authContext = AUTH_CONTEXT_EAP_SIM;
                  break;
  
              case APPTYPE_USIM:
                  authContext = AUTH_CONTEXT_EAP_AKA;
                  break;
  
              default:
                  authContext = AUTH_CONTEXT_UNDEFINED;
                  break;
          }
  
          return authContext;
      }
  
      @UnsupportedAppUsage
      public PersoSubState getPersoSubState() {
          synchronized (mLock) {
              return mPersoSubState;
          }
      }
  
      @UnsupportedAppUsage
      public String getAid() {
          synchronized (mLock) {
              return mAid;
          }
      }
  
      public String getAppLabel() {
          return mAppLabel;
      }
  
      @UnsupportedAppUsage
      public PinState getPin1State() {
          synchronized (mLock) {
              if (mPin1Replaced) {
                  return mUiccProfile.getUniversalPinState();
              }
              return mPin1State;
          }
      }
  
      @UnsupportedAppUsage
      public IccFileHandler getIccFileHandler() {
          synchronized (mLock) {
              return mIccFh;
          }
      }
  
      @UnsupportedAppUsage
      public IccRecords getIccRecords() {
          synchronized (mLock) {
              return mIccRecords;
          }
      }
  
      /**
       * Supply the ICC PIN 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 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
       *
       * 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 supplyPuk (String puk, String newPin, Message onComplete) {
          synchronized (mLock) {
          mCi.supplyIccPukForApp(puk, newPin, mAid,
                  mHandler.obtainMessage(EVENT_PIN1_PUK1_DONE, onComplete));
          }
      }
  
      public void supplyPin2 (String pin2, Message onComplete) {
          synchronized (mLock) {
              mCi.supplyIccPin2ForApp(pin2, mAid,
                      mHandler.obtainMessage(EVENT_PIN2_PUK2_DONE, onComplete));
          }
      }
  
      public void supplyPuk2 (String puk2, String newPin2, Message onComplete) {
          synchronized (mLock) {
              mCi.supplyIccPuk2ForApp(puk2, newPin2, mAid,
                      mHandler.obtainMessage(EVENT_PIN2_PUK2_DONE, onComplete));
          }
      }
  
      public void supplyNetworkDepersonalization (String pin, Message onComplete) {
          synchronized (mLock) {
              if (DBG) log("supplyNetworkDepersonalization");
              mCi.supplyNetworkDepersonalization(pin, onComplete);
          }
      }
  
      /**
       * Check whether ICC pin lock is enabled
       * This is a sync call which returns the cached pin enabled state
       *
       * @return true for ICC locked enabled
       *         false for ICC locked disabled
       */
      public boolean getIccLockEnabled() {
          return mIccLockEnabled;
          /* STOPSHIP: Remove line above and all code associated with setting
             mIccLockEanbled once all RIL correctly sends the pin1 state.
          // Use getPin1State to take into account pin1Replaced flag
          PinState pinState = getPin1State();
          return pinState == PinState.PINSTATE_ENABLED_NOT_VERIFIED ||
                 pinState == PinState.PINSTATE_ENABLED_VERIFIED ||
                 pinState == PinState.PINSTATE_ENABLED_BLOCKED ||
                 pinState == PinState.PINSTATE_ENABLED_PERM_BLOCKED;*/
       }
  
      /**
       * Check whether ICC fdn (fixed dialing number) is enabled
       * This is a sync call which returns the cached pin enabled state
       *
       * @return true for ICC fdn enabled
       *         false for ICC fdn disabled
       */
      public boolean getIccFdnEnabled() {
          synchronized (mLock) {
              return mIccFdnEnabled;
          }
      }
  
      /**
       * 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
       *
       * @param enabled "true" for locked "false" for unlocked.
       * @param password needed to change the ICC pin state, aka. Pin1
       * @param onComplete
       *        onComplete.obj will be an AsyncResult
       *        ((AsyncResult)onComplete.obj).exception == null on success
       *        ((AsyncResult)onComplete.obj).exception != null on fail
       */
      public void setIccLockEnabled (boolean enabled,
              String password, Message onComplete) {
          synchronized (mLock) {
              int serviceClassX;
              serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
                      CommandsInterface.SERVICE_CLASS_DATA +
                      CommandsInterface.SERVICE_CLASS_FAX;
  
              mDesiredPinLocked = enabled;
  
              mCi.setFacilityLockForApp(CommandsInterface.CB_FACILITY_BA_SIM,
                      enabled, password, serviceClassX, mAid,
                      mHandler.obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete));
          }
      }
  
      /**
       * Set the ICC fdn enabled or disabled
       * When the operation is complete, onComplete will be sent to its handler
       *
       * @param enabled "true" for locked "false" for unlocked.
       * @param password needed to change the ICC fdn enable, aka Pin2
       * @param onComplete
       *        onComplete.obj will be an AsyncResult
       *        ((AsyncResult)onComplete.obj).exception == null on success
       *        ((AsyncResult)onComplete.obj).exception != null on fail
       */
      public void setIccFdnEnabled (boolean enabled,
              String password, Message onComplete) {
          synchronized (mLock) {
              int serviceClassX;
              serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
                      CommandsInterface.SERVICE_CLASS_DATA +
                      CommandsInterface.SERVICE_CLASS_FAX +
                      CommandsInterface.SERVICE_CLASS_SMS;
  
              mDesiredFdnEnabled = enabled;
  
              mCi.setFacilityLockForApp(CommandsInterface.CB_FACILITY_BA_FD,
                      enabled, password, serviceClassX, mAid,
                      mHandler.obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete));
          }
      }
  
      /**
       * Change the ICC password used in ICC pin lock
       * When the operation is complete, onComplete will be sent to its handler
       *
       * @param oldPassword is the old password
       * @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
       */
      public void changeIccLockPassword(String oldPassword, String newPassword,
              Message onComplete) {
          synchronized (mLock) {
              if (DBG) log("changeIccLockPassword");
              mCi.changeIccPinForApp(oldPassword, newPassword, mAid,
                      mHandler.obtainMessage(EVENT_CHANGE_PIN1_DONE, onComplete));
          }
      }
  
      /**
       * Change the ICC password used in ICC fdn enable
       * When the operation is complete, onComplete will be sent to its handler
       *
       * @param oldPassword is the old password
       * @param newPassword is the new password
       * @param onComplete
       *        onComplete.obj will be an AsyncResult
       *        ((AsyncResult)onComplete.obj).exception == null on success
       *        ((AsyncResult)onComplete.obj).exception != null on fail
       */
      public void changeIccFdnPassword(String oldPassword, String newPassword,
              Message onComplete) {
          synchronized (mLock) {
              if (DBG) log("changeIccFdnPassword");
              mCi.changeIccPin2ForApp(oldPassword, newPassword, mAid,
                      mHandler.obtainMessage(EVENT_CHANGE_PIN2_DONE, onComplete));
          }
      }
  
      /**
       * @return true if the UiccCardApplication is ready.
       */
      public boolean isReady() {
          synchronized (mLock) {
              if (mAppState != AppState.APPSTATE_READY) {
                  return false;
              } else if (mPin1State == PinState.PINSTATE_ENABLED_NOT_VERIFIED
                      || mPin1State == PinState.PINSTATE_ENABLED_BLOCKED
                      || mPin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) {
                  loge("Sanity check failed! APPSTATE is ready while PIN1 is not verified!!!");
                  return false;
              } else {
                  return true;
              }
          }
      }
  
      /**
       * @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;
          }
      }
  
      @UnsupportedAppUsage
      public int getPhoneId() {
          return mUiccProfile.getPhoneId();
      }
  
      public boolean isAppIgnored() {
          return mIgnoreApp;
      }
  
      public void setAppIgnoreState(boolean ignore) {
          mIgnoreApp = ignore;
      }
  
      protected UiccProfile getUiccProfile() {
          return mUiccProfile;
      }
  
      @UnsupportedAppUsage
      private void log(String msg) {
          Rlog.d(LOG_TAG, msg);
      }
  
      @UnsupportedAppUsage
      private void loge(String msg) {
          Rlog.e(LOG_TAG, msg);
      }
  
      public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
          pw.println("UiccCardApplication: " + this);
          pw.println(" mUiccProfile=" + mUiccProfile);
          pw.println(" mAppState=" + mAppState);
          pw.println(" mAppType=" + mAppType);
          pw.println(" mPersoSubState=" + mPersoSubState);
          pw.println(" mAid=" + mAid);
          pw.println(" mAppLabel=" + mAppLabel);
          pw.println(" mPin1Replaced=" + mPin1Replaced);
          pw.println(" mPin1State=" + mPin1State);
          pw.println(" mPin2State=" + mPin2State);
          pw.println(" mIccFdnEnabled=" + mIccFdnEnabled);
          pw.println(" mDesiredFdnEnabled=" + mDesiredFdnEnabled);
          pw.println(" mIccLockEnabled=" + mIccLockEnabled);
          pw.println(" mDesiredPinLocked=" + mDesiredPinLocked);
          pw.println(" mCi=" + mCi);
          pw.println(" mIccRecords=" + mIccRecords);
          pw.println(" mIccFh=" + mIccFh);
          pw.println(" mDestroyed=" + mDestroyed);
          pw.println(" mReadyRegistrants: size=" + mReadyRegistrants.size());
          for (int i = 0; i < mReadyRegistrants.size(); i++) {
              pw.println("  mReadyRegistrants[" + i + "]="
                      + ((Registrant)mReadyRegistrants.get(i)).getHandler());
          }
          pw.println(" mPinLockedRegistrants: size=" + mPinLockedRegistrants.size());
          for (int i = 0; i < mPinLockedRegistrants.size(); i++) {
              pw.println("  mPinLockedRegistrants[" + i + "]="
                      + ((Registrant)mPinLockedRegistrants.get(i)).getHandler());
          }
          pw.println(" mNetworkLockedRegistrants: size=" + mNetworkLockedRegistrants.size());
          for (int i = 0; i < mNetworkLockedRegistrants.size(); i++) {
              pw.println("  mNetworkLockedRegistrants[" + i + "]="
                      + ((Registrant)mNetworkLockedRegistrants.get(i)).getHandler());
          }
          pw.flush();
      }
  }
  