/**
   * Handles the call to get the subscription source
   *
   * @param ar AsyncResult object that contains the result of get CDMA subscription source call
   */
  private void handleGetCdmaSubscriptionSource(AsyncResult ar) {
    if ((ar.exception == null) && (ar.result != null)) {
      int newSubscriptionSource = ((int[]) ar.result)[0];

      if (newSubscriptionSource != mCdmaSubscriptionSource.get()) {
        log(
            "Subscription Source Changed : "
                + mCdmaSubscriptionSource
                + " >> "
                + newSubscriptionSource);
        mCdmaSubscriptionSource.set(newSubscriptionSource);

        // Save CDMA subscription source
        saveCdmaSubscriptionSource(newSubscriptionSource);

        // Notify registrants of the new CDMA subscription source
        mCdmaSubscriptionSourceChangedRegistrants.notifyRegistrants(
            new AsyncResult(null, null, null));
      }
    } else {
      // GET_CDMA_SUBSCRIPTION is returning Failure. Probably
      // because modem created GSM Phone. If modem created
      // GSMPhone, then PhoneProxy will trigger a change in
      // Phone objects and this object will be destroyed.
      logw(
          "Unable to get CDMA Subscription Source, Exception: "
              + ar.exception
              + ", result: "
              + ar.result);
    }
  }
  /**
   * Handles the call to get the subscription source
   *
   * @param ar AsyncResult object that contains the result of get CDMA subscription source call
   */
  private void handleCdmaSubscriptionSource(AsyncResult ar) {
    if ((ar.exception == null) && (ar.result != null)) {
      int newSubscriptionSource = ((int[]) ar.result)[0];
      boolean cdmaSubSourceChanged = false;

      if (newSubscriptionSource != mCdmaSubscriptionSource) {
        Log.v(
            LOG_TAG,
            "Subscription Source Changed : "
                + mCdmaSubscriptionSource
                + " >> "
                + newSubscriptionSource);
        mCdmaSubscriptionSource = newSubscriptionSource;
        cdmaSubSourceChanged = true;
      }

      if (cdmaSubSourceChanged || mNotifyCdmaSubSource) {
        // Notify registrants of the new CDMA subscription source
        mCdmaSubscriptionSourceChangedRegistrants.notifyRegistrants(
            new AsyncResult(null, null, null));
        if (mNotifyCdmaSubSource) mNotifyCdmaSubSource = false;
      }
    } else {
      // GET_CDMA_SUBSCRIPTION is returning Failure. Probably
      // because modem created GSM Phone. If modem created
      // GSMPhone, then PhoneProxy will trigger a change in
      // Phone objects and this object will be destroyed.
      Log.w(
          LOG_TAG,
          "Unable to get CDMA Subscription Source, Exception: "
              + ar.exception
              + ", result: "
              + ar.result);
    }
  }
  private void updatePhoneState() {
    PhoneConstants.State oldState = state;

    if (ringingCall.isRinging()) {
      state = PhoneConstants.State.RINGING;
    } else if (pendingMO != null || !(foregroundCall.isIdle() && backgroundCall.isIdle())) {
      state = PhoneConstants.State.OFFHOOK;
    } else {
      state = PhoneConstants.State.IDLE;
    }

    if (state == PhoneConstants.State.IDLE && oldState != state) {
      voiceCallEndedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
    } else if (oldState == PhoneConstants.State.IDLE && oldState != state) {
      voiceCallStartedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
    }

    if (state != oldState) {
      phone.notifyPhoneStateChanged();
    }
  }
  private void updatePhoneState() {
    PhoneConstants.State oldState = mState;

    if (mRingingCall.isRinging()) {
      mState = PhoneConstants.State.RINGING;
    } else if (mPendingMO != null || !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
      mState = PhoneConstants.State.OFFHOOK;
    } else {
      mState = PhoneConstants.State.IDLE;
    }

    if (mState == PhoneConstants.State.IDLE && oldState != mState) {
      mVoiceCallEndedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
    } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
      mVoiceCallStartedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
    }

    if (DBG) log("updatePhoneState oldState=" + oldState + ", newState=" + mState);

    if (mState != oldState) {
      mPhone.notifyPhoneStateChanged();
    }
  }
  @Override
  public void handleMessage(Message msg) {
    AsyncResult ar;
    Message onComplete;

    switch (msg.what) {
      case EVENT_RADIO_AVAILABLE:
        {
          mCM.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));

          mCM.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
          mCM.getIMEISV(obtainMessage(EVENT_GET_IMEISV_DONE));
        }
        break;

      case EVENT_RADIO_ON:
        break;

      case EVENT_REGISTERED_TO_NETWORK:
        syncClirSetting();
        break;

      case EVENT_SIM_RECORDS_LOADED:
        updateCurrentCarrierInProvider();

        // Check if this is a different SIM than the previous one. If so unset the
        // voice mail number.
        String imsi = getVmSimImsi();
        String imsiFromSIM = getSubscriberId();
        if (imsi != null && imsiFromSIM != null && !imsiFromSIM.equals(imsi)) {
          storeVoiceMailNumber(null);
          setVmSimImsi(null);
        }

        break;

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

        if (ar.exception != null) {
          break;
        }

        if (LOCAL_DEBUG) Log.d(LOG_TAG, "Baseband version: " + ar.result);
        setSystemProperty(PROPERTY_BASEBAND_VERSION, (String) ar.result);
        break;

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

        if (ar.exception != null) {
          break;
        }

        mImei = (String) ar.result;
        break;

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

        if (ar.exception != null) {
          break;
        }

        mImeiSv = (String) ar.result;
        break;

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

        String[] ussdResult = (String[]) ar.result;

        if (ussdResult.length > 1) {
          try {
            onIncomingUSSD(Integer.parseInt(ussdResult[0]), ussdResult[1]);
          } catch (NumberFormatException e) {
            Log.w(LOG_TAG, "error parsing USSD");
          }
        }
        break;

      case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
        // Some MMI requests (eg USSD) are not completed
        // within the course of a CommandsInterface request
        // If the radio shuts off or resets while one of these
        // is pending, we need to clean up.

        for (int i = 0, s = mPendingMMIs.size(); i < s; i++) {
          if (mPendingMMIs.get(i).isPendingUSSD()) {
            mPendingMMIs.get(i).onUssdFinishedError();
          }
        }
        break;

      case EVENT_SSN:
        ar = (AsyncResult) msg.obj;
        SuppServiceNotification not = (SuppServiceNotification) ar.result;
        mSsnRegistrants.notifyRegistrants(ar);
        break;

      case EVENT_SET_CALL_FORWARD_DONE:
        ar = (AsyncResult) msg.obj;
        if (ar.exception == null) {
          mSIMRecords.setVoiceCallForwardingFlag(1, msg.arg1 == 1);
        }
        onComplete = (Message) ar.userObj;
        if (onComplete != null) {
          AsyncResult.forMessage(onComplete, ar.result, ar.exception);
          onComplete.sendToTarget();
        }
        break;

      case EVENT_SET_VM_NUMBER_DONE:
        ar = (AsyncResult) msg.obj;
        if (IccVmNotSupportedException.class.isInstance(ar.exception)) {
          storeVoiceMailNumber(mVmNumber);
          ar.exception = null;
        }
        onComplete = (Message) ar.userObj;
        if (onComplete != null) {
          AsyncResult.forMessage(onComplete, ar.result, ar.exception);
          onComplete.sendToTarget();
        }
        break;

      case EVENT_GET_CALL_FORWARD_DONE:
        ar = (AsyncResult) msg.obj;
        if (ar.exception == null) {
          handleCfuQueryResult((CallForwardInfo[]) ar.result);
        }
        onComplete = (Message) ar.userObj;
        if (onComplete != null) {
          AsyncResult.forMessage(onComplete, ar.result, ar.exception);
          onComplete.sendToTarget();
        }
        break;

        // handle the select network completion callbacks.
      case EVENT_SET_NETWORK_MANUAL_COMPLETE:
      case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE:
        handleSetSelectNetwork((AsyncResult) msg.obj);
        break;

      case EVENT_SET_CLIR_COMPLETE:
        ar = (AsyncResult) msg.obj;
        if (ar.exception == null) {
          saveClirSetting(msg.arg1);
        }
        onComplete = (Message) ar.userObj;
        if (onComplete != null) {
          AsyncResult.forMessage(onComplete, ar.result, ar.exception);
          onComplete.sendToTarget();
        }
        break;

      default:
        super.handleMessage(msg);
    }
  }
  /*
   * Process the response for the RIL request GET_DATA_CALL_PROFILE.
   * Save the profile details received.
   */
  private void onGetDataCallProfileDone(AsyncResult ar, int context) {
    if (ar.exception != null) {
      log("OMH: Exception in onGetDataCallProfileDone:" + ar.exception);
      return;
    }

    if (context != mOmhReadProfileContext) {
      // we have other onReadOmhDataprofiles() on the way.
      return;
    }

    // DataProfile list from the modem for a given SERVICE_TYPE. These may
    // be from RUIM in case of OMH
    ArrayList<DataProfile> dataProfileListModem = new ArrayList<DataProfile>();
    dataProfileListModem = (ArrayList<DataProfile>) ar.result;

    DataProfileTypeModem modemProfile = (DataProfileTypeModem) ar.userObj;

    mOmhReadProfileCount--;

    if (dataProfileListModem != null && dataProfileListModem.size() > 0) {
      String serviceType;

      /* For the modem service type, get the android DataServiceType */
      serviceType = modemProfile.getDataServiceType();

      log(
          "OMH: # profiles returned from modem:"
              + dataProfileListModem.size()
              + " for "
              + serviceType);

      mOmhServicePriorityMap.put(
          serviceType, omhListGetArbitratedPriority(dataProfileListModem, serviceType));

      for (DataProfile dp : dataProfileListModem) {

        /* Store the modem profile type in the data profile */
        ((DataProfileOmh) dp).setDataProfileTypeModem(modemProfile);

        /* Look through mTempOmhDataProfilesList for existing profile id's
         * before adding it. This implies that the (similar) profile with same
         * priority already exists.
         */
        DataProfileOmh omhDuplicatedp = getDuplicateProfile(dp);
        if (null == omhDuplicatedp) {
          mTempOmhDataProfilesList.add(dp);
          ((DataProfileOmh) dp)
              .addServiceType(DataProfileTypeModem.getDataProfileTypeModem(serviceType));
        } else {
          /*  To share the already established data connection
           * (say between SUPL and DUN) in cases such as below:
           *  Ex:- SUPL+DUN [profile id 201, priority 1]
           *  'dp' instance is found at this point. Add the non-provisioned
           *   service type to this 'dp' instance
           */
          log("OMH: Duplicate Profile " + omhDuplicatedp);
          ((DataProfileOmh) omhDuplicatedp)
              .addServiceType(DataProfileTypeModem.getDataProfileTypeModem(serviceType));
        }
      }
    }

    // (Re)Load APN List
    if (mOmhReadProfileCount == 0) {
      log("OMH: Modem omh profile read complete.");
      addServiceTypeToUnSpecified();
      mDataProfilesList.addAll(mTempOmhDataProfilesList);
      mModemDataProfileRegistrants.notifyRegistrants();
    }

    return;
  }