// inherited javadoc suffices
  public void cancel() {
    // Complete or failed cannot be cancelled
    if (state == State.COMPLETE || state == State.FAILED) {
      return;
    }

    state = State.CANCELLED;

    if (isPendingUSSD) {
      /*
       * There can only be one pending USSD session, so tell the radio to
       * cancel it.
       */
      phone.mCM.cancelPendingUssd(obtainMessage(EVENT_USSD_CANCEL_COMPLETE, this));

      /*
       * Don't call phone.onMMIDone here; wait for CANCEL_COMPLETE notice
       * from RIL.
       */
    } else {
      // TODO in cases other than USSD, it would be nice to cancel
      // the pending radio operation. This requires RIL cancellation
      // support, which does not presently exist.

      phone.onMMIDone(this);
    }
  }
  private void onQueryComplete(AsyncResult ar) {
    StringBuilder sb = new StringBuilder(getScString());
    sb.append("\n");

    if (ar.exception != null) {
      state = State.FAILED;
      sb.append(getErrorMessage(ar));
    } else {
      int[] ints = (int[]) ar.result;

      if (ints.length != 0) {
        if (ints[0] == 0) {
          sb.append(context.getText(com.android.internal.R.string.serviceDisabled));
        } else if (sc.equals(SC_WAIT)) {
          // Call Waiting includes additional data in the response.
          sb.append(createQueryCallWaitingResultMessage(ints[1]));
        } else if (isServiceCodeCallBarring(sc)) {
          // ints[0] for Call Barring is a bit vector of services
          sb.append(createQueryCallBarringResultMessage(ints[0]));
        } else if (ints[0] == 1) {
          // for all other services, treat it as a boolean
          sb.append(context.getText(com.android.internal.R.string.serviceEnabled));
        } else {
          sb.append(context.getText(com.android.internal.R.string.mmiError));
        }
      } else {
        sb.append(context.getText(com.android.internal.R.string.mmiError));
      }
      state = State.COMPLETE;
    }

    message = sb;
    phone.onMMIDone(this);
  }
  /**
   * Called from GSMPhone
   *
   * <p>The radio has reset, and this is still pending
   */
  void onUssdFinishedError() {
    if (state == State.PENDING) {
      state = State.FAILED;
      message = context.getText(com.android.internal.R.string.mmiError);

      phone.onMMIDone(this);
    }
  }
 private void handlePasswordError(int res) {
   state = State.FAILED;
   StringBuilder sb = new StringBuilder(getScString());
   sb.append("\n");
   sb.append(context.getText(res));
   message = sb;
   phone.onMMIDone(this);
 }
  private void onQueryCfComplete(AsyncResult ar) {
    StringBuilder sb = new StringBuilder(getScString());
    sb.append("\n");

    if (ar.exception != null) {
      state = State.FAILED;
      sb.append(getErrorMessage(ar));
    } else {
      CallForwardInfo infos[];

      infos = (CallForwardInfo[]) ar.result;

      if (infos.length == 0) {
        // Assume the default is not active
        sb.append(context.getText(com.android.internal.R.string.serviceDisabled));

        // Set unconditional CFF in SIM to false
        if (phone.mSIMRecords != null) {
          phone.setCallForwardingPreference(false);
          phone.mSIMRecords.setVoiceCallForwardingFlag(1, false);
        } else {
          Log.w(LOG_TAG, "setVoiceCallForwardingFlag aborted. sim records is null.");
        }
      } else {

        SpannableStringBuilder tb = new SpannableStringBuilder();

        // Each bit in the service class gets its own result line
        // The service classes may be split up over multiple
        // CallForwardInfos. So, for each service class, find out
        // which CallForwardInfo represents it and then build
        // the response text based on that

        for (int serviceClassMask = 1;
            serviceClassMask <= SERVICE_CLASS_MAX;
            serviceClassMask <<= 1) {
          for (int i = 0, s = infos.length; i < s; i++) {
            if ((serviceClassMask & infos[i].serviceClass) != 0) {
              tb.append(makeCFQueryResultMessage(infos[i], serviceClassMask));
              tb.append("\n");
            }
          }
        }
        sb.append(tb);
      }

      state = State.COMPLETE;
    }

    message = sb;
    phone.onMMIDone(this);
  }
  /**
   * Called from GSMPhone
   *
   * <p>An unsolicited USSD NOTIFY or REQUEST has come in matching up with this pending USSD request
   *
   * <p>Note: If REQUEST, this exchange is complete, but the session remains active (ie, the network
   * expects user input).
   */
  void onUssdFinished(String ussdMessage, boolean isUssdRequest) {
    if (state == State.PENDING) {
      if (ussdMessage == null) {
        message = context.getText(com.android.internal.R.string.mmiComplete);
      } else {
        message = ussdMessage;
      }
      this.isUssdRequest = isUssdRequest;
      // If it's a request, leave it PENDING so that it's cancelable.
      if (!isUssdRequest) {
        state = State.COMPLETE;
      }

      phone.onMMIDone(this);
    }
  }
  private void onSetComplete(AsyncResult ar) {
    StringBuilder sb = new StringBuilder(getScString());
    sb.append("\n");

    if (ar.exception != null) {
      state = State.FAILED;
      if (ar.exception instanceof CommandException) {
        CommandException.Error err = ((CommandException) (ar.exception)).getCommandError();
        if (err == CommandException.Error.PASSWORD_INCORRECT) {
          if (isPinCommand()) {
            // look specifically for the PUK commands and adjust
            // the message accordingly.
            if (sc.equals(SC_PUK) || sc.equals(SC_PUK2)) {
              sb.append(context.getText(com.android.internal.R.string.badPuk));
            } else {
              sb.append(context.getText(com.android.internal.R.string.badPin));
            }
          } else {
            sb.append(context.getText(com.android.internal.R.string.passwordIncorrect));
          }
        } else if (err == CommandException.Error.SIM_PUK2) {
          sb.append(context.getText(com.android.internal.R.string.badPin));
          sb.append("\n");
          sb.append(context.getText(com.android.internal.R.string.needPuk2));
        } else if (err == CommandException.Error.FDN_CHECK_FAILURE) {
          Log.i(LOG_TAG, "FDN_CHECK_FAILURE");
          sb.append(context.getText(com.android.internal.R.string.mmiFdnError));
        } else {
          sb.append(context.getText(com.android.internal.R.string.mmiError));
        }
      } else {
        sb.append(context.getText(com.android.internal.R.string.mmiError));
      }
    } else if (isActivate()) {
      state = State.COMPLETE;
      if (isCallFwdRegister) {
        sb.append(context.getText(com.android.internal.R.string.serviceRegistered));
        isCallFwdRegister = false;
      } else {
        sb.append(context.getText(com.android.internal.R.string.serviceEnabled));
      }
      // Record CLIR setting
      if (sc.equals(SC_CLIR)) {
        phone.saveClirSetting(CommandsInterface.CLIR_INVOCATION);
      }
    } else if (isDeactivate()) {
      state = State.COMPLETE;
      sb.append(context.getText(com.android.internal.R.string.serviceDisabled));
      // Record CLIR setting
      if (sc.equals(SC_CLIR)) {
        phone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION);
      }
    } else if (isRegister()) {
      state = State.COMPLETE;
      sb.append(context.getText(com.android.internal.R.string.serviceRegistered));
    } else if (isErasure()) {
      state = State.COMPLETE;
      sb.append(context.getText(com.android.internal.R.string.serviceErased));
    } else {
      state = State.FAILED;
      sb.append(context.getText(com.android.internal.R.string.mmiError));
    }

    message = sb;
    phone.onMMIDone(this);
  }
  /** Called from GSMPhone.handleMessage; not a Handler subclass */
  public void handleMessage(Message msg) {
    AsyncResult ar;

    switch (msg.what) {
      case EVENT_SET_COMPLETE:
        ar = (AsyncResult) (msg.obj);

        onSetComplete(ar);
        break;

      case EVENT_SET_CFF_COMPLETE:
        ar = (AsyncResult) (msg.obj);

        /*
         * msg.arg1 = 1 means to set unconditional voice call forwarding
         * msg.arg2 = 1 means to enable voice call forwarding
         */
        if ((ar.exception == null) && (msg.arg1 == 1)) {
          boolean cffEnabled = (msg.arg2 == 1);
          if (phone.mSIMRecords != null) {
            phone.mSIMRecords.setVoiceCallForwardingFlag(1, cffEnabled);
            phone.setCallForwardingPreference(cffEnabled);
          } else {
            Log.w(LOG_TAG, "setVoiceCallForwardingFlag aborted. sim records is null.");
          }
        }

        onSetComplete(ar);
        break;

      case EVENT_GET_CLIR_COMPLETE:
        ar = (AsyncResult) (msg.obj);
        onGetClirComplete(ar);
        break;

      case EVENT_QUERY_CF_COMPLETE:
        ar = (AsyncResult) (msg.obj);
        onQueryCfComplete(ar);
        break;

      case EVENT_QUERY_COMPLETE:
        ar = (AsyncResult) (msg.obj);
        onQueryComplete(ar);
        break;

      case EVENT_USSD_COMPLETE:
        ar = (AsyncResult) (msg.obj);

        if (ar.exception != null) {
          state = State.FAILED;
          message = getErrorMessage(ar);

          phone.onMMIDone(this);
        }

        // Note that unlike most everything else, the USSD complete
        // response does not complete this MMI code...we wait for
        // an unsolicited USSD "Notify" or "Request".
        // The matching up of this is done in GSMPhone.

        break;

      case EVENT_USSD_CANCEL_COMPLETE:
        phone.onMMIDone(this);
        break;
    }
  }
  /** Process a MMI code or short code...anything that isn't a dialing number */
  void processCode() {
    try {
      if (isShortCode()) {
        Log.d(LOG_TAG, "isShortCode");
        // These just get treated as USSD.
        sendUssd(dialingNumber);
      } else if (dialingNumber != null) {
        // We should have no dialing numbers here
        throw new RuntimeException("Invalid or Unsupported MMI Code");
      } else if (sc != null && sc.equals(SC_CLIP)) {
        Log.d(LOG_TAG, "is CLIP");
        if (isInterrogate()) {
          phone.mCM.queryCLIP(obtainMessage(EVENT_QUERY_COMPLETE, this));
        } else {
          throw new RuntimeException("Invalid or Unsupported MMI Code");
        }
      } else if (sc != null && sc.equals(SC_CLIR)) {
        Log.d(LOG_TAG, "is CLIR");
        if (isActivate()) {
          phone.mCM.setCLIR(
              CommandsInterface.CLIR_INVOCATION, obtainMessage(EVENT_SET_COMPLETE, this));
        } else if (isDeactivate()) {
          phone.mCM.setCLIR(
              CommandsInterface.CLIR_SUPPRESSION, obtainMessage(EVENT_SET_COMPLETE, this));
        } else if (isInterrogate()) {
          phone.mCM.getCLIR(obtainMessage(EVENT_GET_CLIR_COMPLETE, this));
        } else {
          throw new RuntimeException("Invalid or Unsupported MMI Code");
        }
      } else if (isServiceCodeCallForwarding(sc)) {
        Log.d(LOG_TAG, "is CF");

        String dialingNumber = sia;
        int serviceClass = siToServiceClass(sib);
        int reason = scToCallForwardReason(sc);
        int time = siToTime(sic);

        if (isInterrogate()) {
          phone.mCM.queryCallForwardStatus(
              reason, serviceClass, dialingNumber, obtainMessage(EVENT_QUERY_CF_COMPLETE, this));
        } else {
          int cfAction;

          if (isActivate()) {
            if (dialingNumber != null) {
              isCallFwdRegister = true;
              cfAction = CommandsInterface.CF_ACTION_REGISTRATION;
            } else {
              cfAction = CommandsInterface.CF_ACTION_ENABLE;
            }
          } else if (isDeactivate()) {
            cfAction = CommandsInterface.CF_ACTION_DISABLE;
          } else if (isRegister()) {
            cfAction = CommandsInterface.CF_ACTION_REGISTRATION;
          } else if (isErasure()) {
            cfAction = CommandsInterface.CF_ACTION_ERASURE;
          } else {
            throw new RuntimeException("invalid action");
          }

          int isSettingUnconditionalVoice =
              (((reason == CommandsInterface.CF_REASON_UNCONDITIONAL)
                          || (reason == CommandsInterface.CF_REASON_ALL))
                      && (((serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0)
                          || (serviceClass == CommandsInterface.SERVICE_CLASS_NONE)))
                  ? 1
                  : 0;

          int isEnableDesired =
              ((cfAction == CommandsInterface.CF_ACTION_ENABLE)
                      || (cfAction == CommandsInterface.CF_ACTION_REGISTRATION))
                  ? 1
                  : 0;

          Log.d(LOG_TAG, "is CF setCallForward");
          phone.mCM.setCallForward(
              cfAction,
              reason,
              serviceClass,
              dialingNumber,
              time,
              obtainMessage(
                  EVENT_SET_CFF_COMPLETE, isSettingUnconditionalVoice, isEnableDesired, this));
        }
      } else if (isServiceCodeCallBarring(sc)) {
        // sia = password
        // sib = basic service group

        String password = sia;
        int serviceClass = siToServiceClass(sib);
        String facility = scToBarringFacility(sc);

        if (isInterrogate()) {
          phone.mCM.queryFacilityLock(
              0, "", facility, password, serviceClass, obtainMessage(EVENT_QUERY_COMPLETE, this));
        } else if (isActivate() || isDeactivate()) {
          phone.mCM.setFacilityLock(
              0,
              "",
              facility,
              isActivate(),
              password,
              serviceClass,
              obtainMessage(EVENT_SET_COMPLETE, this));
        } else {
          throw new RuntimeException("Invalid or Unsupported MMI Code");
        }

      } else if (sc != null && sc.equals(SC_PWD)) {
        // sia = fac
        // sib = old pwd
        // sic = new pwd
        // pwd = new pwd
        String facility;
        String oldPwd = sib;
        String newPwd = sic;
        if (isActivate() || isRegister()) {
          // Even though ACTIVATE is acceptable, this is really termed a REGISTER
          action = ACTION_REGISTER;

          if (sia == null) {
            // If sc was not specified, treat it as BA_ALL.
            facility = CommandsInterface.CB_FACILITY_BA_ALL;
          } else {
            facility = scToBarringFacility(sia);
          }
          if (newPwd.equals(pwd)) {
            phone.mCM.changeBarringPassword(
                facility, oldPwd, newPwd, obtainMessage(EVENT_SET_COMPLETE, this));
          } else {
            // password mismatch; return error
            handlePasswordError(com.android.internal.R.string.passwordIncorrect);
          }
        } else {
          throw new RuntimeException("Invalid or Unsupported MMI Code");
        }

      } else if (sc != null && sc.equals(SC_WAIT)) {
        // sia = basic service group
        int serviceClass = siToServiceClass(sia);

        if (isActivate() || isDeactivate()) {
          phone.mCM.setCallWaiting(
              isActivate(), serviceClass, obtainMessage(EVENT_SET_COMPLETE, this));
        } else if (isInterrogate()) {
          phone.mCM.queryCallWaiting(serviceClass, obtainMessage(EVENT_QUERY_COMPLETE, this));
        } else {
          throw new RuntimeException("Invalid or Unsupported MMI Code");
        }
      } else if (isPinCommand()) {
        // sia = old PIN or PUK
        // sib = new PIN
        // sic = new PIN
        String oldPinOrPuk = sia;
        String newPin = sib;
        int pinLen = newPin.length();
        if (isRegister()) {
          if (!newPin.equals(sic)) {
            // 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 (sc.equals(SC_PIN)
              && phone.m3gppApplication != null
              && phone.m3gppApplication.getState() == UiccConstants.AppState.APPSTATE_PUK) {
            // Sim is puk-locked
            handlePasswordError(com.android.internal.R.string.needPuk);
          } else {
            // pre-checks OK
            if (sc.equals(SC_PIN)) {
              if (mUiccApp != null)
                mUiccApp.changeIccLockPassword(
                    oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this));
            } else if (sc.equals(SC_PIN2)) {
              if (mUiccApp != null)
                mUiccApp.changeIccFdnPassword(
                    oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this));
            } else if (sc.equals(SC_PUK)) {
              if (mUiccApp != null)
                mUiccApp.supplyPuk(oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this));
            } else if (sc.equals(SC_PUK2)) {
              if (mUiccApp != null)
                mUiccApp.supplyPuk2(oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this));
            }
          }
        } else {
          throw new RuntimeException("Invalid or Unsupported MMI Code");
        }
      } else if (isServiceCodeUnsupported(sc)) {
        Log.d(LOG_TAG, "Unsupported MMI code: " + sc);
        state = State.FAILED;
        message = context.getText(com.android.internal.R.string.unsupportedMmiCode);
        phone.onMMIDone(this);
      } else if (poundString != null) {
        sendUssd(poundString);
      } else {
        throw new RuntimeException("Invalid or Unsupported MMI Code");
      }
    } catch (RuntimeException exc) {
      state = State.FAILED;
      message = context.getText(com.android.internal.R.string.mmiError);
      phone.onMMIDone(this);
    }
  }
  private void onGetClirComplete(AsyncResult ar) {
    StringBuilder sb = new StringBuilder(getScString());
    sb.append("\n");

    if (ar.exception != null) {
      state = State.FAILED;
      sb.append(getErrorMessage(ar));
    } else {
      int clirArgs[];

      clirArgs = (int[]) ar.result;

      // the 'm' parameter from TS 27.007 7.7
      switch (clirArgs[1]) {
        case 0: // CLIR not provisioned
          sb.append(context.getText(com.android.internal.R.string.serviceNotProvisioned));
          state = State.COMPLETE;
          break;

        case 1: // CLIR provisioned in permanent mode
          sb.append(context.getText(com.android.internal.R.string.CLIRPermanent));
          state = State.COMPLETE;
          break;

        case 2: // unknown (e.g. no network, etc.)
          sb.append(context.getText(com.android.internal.R.string.mmiError));
          state = State.FAILED;
          break;

        case 3: // CLIR temporary mode presentation restricted

          // the 'n' parameter from TS 27.007 7.7
          switch (clirArgs[0]) {
            default:
            case 0: // Default
              sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOnNextCallOn));
              break;
            case 1: // CLIR invocation
              sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOnNextCallOn));
              break;
            case 2: // CLIR suppression
              sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOnNextCallOff));
              break;
          }
          state = State.COMPLETE;
          break;

        case 4: // CLIR temporary mode presentation allowed
          // the 'n' parameter from TS 27.007 7.7
          switch (clirArgs[0]) {
            default:
            case 0: // Default
              sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOffNextCallOff));
              break;
            case 1: // CLIR invocation
              sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOffNextCallOn));
              break;
            case 2: // CLIR suppression
              sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOffNextCallOff));
              break;
          }

          state = State.COMPLETE;
          break;
      }
    }

    message = sb;
    phone.onMMIDone(this);
  }