// Workaround for Samsung CDMA "ring of death" bug:
  //
  // Symptom: As soon as the phone receives notice of an incoming call, an
  // audible "old fashioned ring" is emitted through the earpiece and
  // persists through the duration of the call, or until reboot if the call
  // isn't answered.
  //
  // Background: The CDMA telephony stack implements a number of "signal info
  // tones" that are locally generated by ToneGenerator and mixed into the
  // voice call path in response to radio RIL_UNSOL_CDMA_INFO_REC requests.
  // One of these tones, IS95_CONST_IR_SIG_IS54B_L, is requested by the
  // radio just prior to notice of an incoming call when the voice call
  // path is muted. CallNotifier is responsible for stopping all signal
  // tones (by "playing" the TONE_CDMA_SIGNAL_OFF tone) upon receipt of a
  // "new ringing connection", prior to unmuting the voice call path.
  //
  // Problem: CallNotifier's incoming call path is designed to minimize
  // latency to notify users of incoming calls ASAP. Thus,
  // SignalInfoTonePlayer requests are handled asynchronously by spawning a
  // one-shot thread for each. Unfortunately the ToneGenerator API does
  // not provide a mechanism to specify an ordering on requests, and thus,
  // unexpected thread interleaving may result in ToneGenerator processing
  // them in the opposite order that CallNotifier intended. In this case,
  // playing the "signal off" tone first, followed by playing the "old
  // fashioned ring" indefinitely.
  //
  // Solution: An API change to ToneGenerator is required to enable
  // SignalInfoTonePlayer to impose an ordering on requests (i.e., drop any
  // request that's older than the most recent observed). Such a change,
  // or another appropriate fix should be implemented in AOSP first.
  //
  // Workaround: Intercept RIL_UNSOL_CDMA_INFO_REC requests from the radio,
  // check for a signal info record matching IS95_CONST_IR_SIG_IS54B_L, and
  // drop it so it's never seen by CallNotifier. If other signal tones are
  // observed to cause this problem, they should be dropped here as well.
  @Override
  protected void notifyRegistrantsCdmaInfoRec(CdmaInformationRecords infoRec) {
    final int response = RIL_UNSOL_CDMA_INFO_REC;

    if (infoRec.record instanceof CdmaSignalInfoRec) {
      CdmaSignalInfoRec sir = (CdmaSignalInfoRec) infoRec.record;
      if (sir != null
          && sir.isPresent
          && sir.signalType == SignalToneUtil.IS95_CONST_IR_SIGNAL_IS54B
          && sir.alertPitch == SignalToneUtil.IS95_CONST_IR_ALERT_MED
          && sir.signal == SignalToneUtil.IS95_CONST_IR_SIG_IS54B_L) {

        Rlog.d(
            RILJ_LOG_TAG,
            "Dropping \""
                + responseToString(response)
                + " "
                + retToString(response, sir)
                + "\" to prevent \"ring of death\" bug.");
        return;
      }
    }

    super.notifyRegistrantsCdmaInfoRec(infoRec);
  }
  @Override
  public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
    if (!dialCode) {
      super.dial(address, clirMode, uusInfo, result);
      return;
    }
    RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);

    rr.mParcel.writeString(address);
    rr.mParcel.writeInt(clirMode);
    rr.mParcel.writeInt(0);
    rr.mParcel.writeInt(1);
    rr.mParcel.writeString("");

    if (uusInfo == null) {
      rr.mParcel.writeInt(0); // UUS information is absent
    } else {
      rr.mParcel.writeInt(1); // UUS information is present
      rr.mParcel.writeInt(uusInfo.getType());
      rr.mParcel.writeInt(uusInfo.getDcs());
      rr.mParcel.writeByteArray(uusInfo.getUserData());
    }

    if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

    send(rr);
  }
 static String requestToString(int request) {
   switch (request) {
     case RIL_REQUEST_DIAL_EMERGENCY:
       return "DIAL_EMERGENCY";
     default:
       return RIL.requestToString(request);
   }
 }
  @Override
  protected void processUnsolicited(Parcel p) {
    Object ret;
    int dataPosition = p.dataPosition(); // save off position within the Parcel
    int response = p.readInt();

    switch (response) {
      case RIL_UNSOL_RIL_CONNECTED: // Fix for NV/RUIM setting on CDMA SIM devices
        // skip getcdmascriptionsource as if qualcomm handles it in the ril binary
        ret = responseInts(p);
        setRadioPower(false, null);
        setPreferredNetworkType(mPreferredNetworkType, null);
        int cdmaSubscription =
            Settings.Global.getInt(
                mContext.getContentResolver(), Settings.Global.CDMA_SUBSCRIPTION_MODE, -1);
        if (cdmaSubscription != -1) {
          setCdmaSubscriptionSource(mCdmaSubscription, null);
        }
        setCellInfoListRate(Integer.MAX_VALUE, null);
        notifyRegistrantsRilConnectionChanged(((int[]) ret)[0]);
        break;
      case RIL_UNSOL_NITZ_TIME_RECEIVED:
        handleNitzTimeReceived(p);
        break;
        // SAMSUNG STATES
      case SamsungExynos4RIL.RIL_UNSOL_AM:
        ret = responseString(p);
        String amString = (String) ret;
        Rlog.d(RILJ_LOG_TAG, "Executing AM: " + amString);

        try {
          Runtime.getRuntime().exec("am " + amString);
        } catch (IOException e) {
          e.printStackTrace();
          Rlog.e(RILJ_LOG_TAG, "am " + amString + " could not be executed.");
        }
        break;
      case SamsungExynos4RIL.RIL_UNSOL_RESPONSE_HANDOVER:
        ret = responseVoid(p);
        break;
      case 1036:
        ret = responseVoid(p);
        break;
      case SamsungExynos4RIL.RIL_UNSOL_WB_AMR_STATE:
        ret = responseInts(p);
        setWbAmr(((int[]) ret)[0]);
        break;
      default:
        // Rewind the Parcel
        p.setDataPosition(dataPosition);

        // Forward responses that we are not overriding to the super class
        super.processUnsolicited(p);
        return;
    }
  }
  @Override
  protected RadioState getRadioStateFromInt(int stateInt) {
    if (!oldRilState) super.getRadioStateFromInt(stateInt);
    RadioState state;

    /* RIL_RadioState ril.h */
    switch (stateInt) {
      case 0:
        state = RadioState.RADIO_OFF;
        break;
      case 1:
      case 2:
        state = RadioState.RADIO_UNAVAILABLE;
        break;
      case 4:
        // When SIM is PIN-unlocked, RIL doesn't respond with RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED.
        // We notify the system here.
        Rlog.d(RILJ_LOG_TAG, "SIM is PIN-unlocked now");
        if (mIccStatusChangedRegistrants != null) {
          mIccStatusChangedRegistrants.notifyRegistrants();
        }
      case 3:
      case 5:
      case 6:
      case 7:
      case 8:
      case 9:
      case 10:
      case 13:
        state = RadioState.RADIO_ON;
        break;

      default:
        throw new RuntimeException("Unrecognized RIL_RadioState: " + stateInt);
    }
    return state;
  }
  @Override
  protected void processUnsolicited(Parcel p) {
    Object ret;
    int dataPosition = p.dataPosition();
    int response = p.readInt();

    switch (response) {
      case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
        ret = responseVoid(p);
        break;
      case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS:
        ret = responseString(p);
        break;
      case RIL_UNSOL_RIL_CONNECTED:
        ret = responseInts(p);
        break;
        // SAMSUNG STATES
      case RIL_UNSOL_AM:
        ret = responseString(p);
        break;
      case RIL_UNSOL_DUN_PIN_CONTROL_SIGNAL:
        ret = responseVoid(p);
        break;
      case RIL_UNSOL_DATA_SUSPEND_RESUME:
        ret = responseInts(p);
        break;
      case RIL_UNSOL_STK_CALL_CONTROL_RESULT:
        ret = responseVoid(p);
        break;
      case RIL_UNSOL_TWO_MIC_STATE:
        ret = responseInts(p);
        break;
      case RIL_UNSOL_WB_AMR_STATE:
        ret = responseInts(p);
        break;

      default:
        // Rewind the Parcel
        p.setDataPosition(dataPosition);

        // Forward responses that we are not overriding to the super class
        super.processUnsolicited(p);
        return;
    }

    switch (response) {
      case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
        /* has bonus radio state int */
        int state = p.readInt();
        Log.d(LOG_TAG, "Radio state: " + state);

        switch (state) {
          case 2:
            // RADIO_UNAVAILABLE
            state = 1;
            break;
          case 3:
            // RADIO_ON
            state = 10;
            break;
          case 4:
            // RADIO_ON
            state = 10;
            // When SIM is PIN-unlocked, RIL doesn't respond with
            // RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED.
            // We notify the system here.
            Log.d(LOG_TAG, "SIM is PIN-unlocked now");
            if (mIccStatusChangedRegistrants != null) {
              mIccStatusChangedRegistrants.notifyRegistrants();
            }
            break;
        }
        RadioState newState = getRadioStateFromInt(state);
        Log.d(LOG_TAG, "New Radio state: " + state + " (" + newState.toString() + ")");
        switchToRadioState(newState);
        break;
      case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS:
        if (RILJ_LOGD) unsljLogRet(response, ret);

        if (mGsmBroadcastSmsRegistrant != null) {
          mGsmBroadcastSmsRegistrant.notifyRegistrant(new AsyncResult(null, ret, null));
        }
        break;
      case RIL_UNSOL_RIL_CONNECTED:
        if (RILJ_LOGD) unsljLogRet(response, ret);

        // Initial conditions
        setRadioPower(false, null);
        sendPreferedNetworktype(mPreferredNetworkType, null);
        setCdmaSubscriptionSource(mCdmaSubscription, null);
        notifyRegistrantsRilConnectionChanged(((int[]) ret)[0]);
        break;
        // SAMSUNG STATES
      case RIL_UNSOL_AM:
        if (RILJ_LOGD) samsungUnsljLogRet(response, ret);
        String amString = (String) ret;
        Log.d(LOG_TAG, "Executing AM: " + amString);

        try {
          Runtime.getRuntime().exec("am " + amString);
        } catch (IOException e) {
          e.printStackTrace();
          Log.e(LOG_TAG, "am " + amString + " could not be executed.");
        }
        break;
      case RIL_UNSOL_DUN_PIN_CONTROL_SIGNAL:
        if (RILJ_LOGD) samsungUnsljLogRet(response, ret);
        break;
      case RIL_UNSOL_DATA_SUSPEND_RESUME:
        if (RILJ_LOGD) samsungUnsljLogRet(response, ret);
        break;
      case RIL_UNSOL_STK_CALL_CONTROL_RESULT:
        if (RILJ_LOGD) samsungUnsljLogRet(response, ret);
        break;
      case RIL_UNSOL_TWO_MIC_STATE:
        if (RILJ_LOGD) samsungUnsljLogRet(response, ret);
        break;
      case RIL_UNSOL_WB_AMR_STATE:
        if (RILJ_LOGD) samsungUnsljLogRet(response, ret);
        setWbAmr(((int[]) ret)[0]);
        break;
    }
  }
 @Override
 public void setPhoneType(int phoneType) {
   super.setPhoneType(phoneType);
   isGSM = (phoneType != RILConstants.CDMA_PHONE);
 }
 @Override
 public void sendSMS(String smscPDU, String pdu, Message result) {
   smsLock();
   super.sendSMS(smscPDU, pdu, result);
 }
 @Override
 public void sendCdmaSms(byte[] pdu, Message result) {
   smsLock();
   super.sendCdmaSms(pdu, result);
 }