content: /*
   * Copyright (C) 2006 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;
  
  import static com.android.internal.telephony.RILConstants.*;
  import static com.android.internal.util.Preconditions.checkNotNull;
  
  import android.annotation.UnsupportedAppUsage;
  import android.content.Context;
  import android.hardware.radio.V1_0.Carrier;
  import android.hardware.radio.V1_0.CarrierRestrictions;
  import android.hardware.radio.V1_0.CdmaBroadcastSmsConfigInfo;
  import android.hardware.radio.V1_0.CdmaSmsAck;
  import android.hardware.radio.V1_0.CdmaSmsMessage;
  import android.hardware.radio.V1_0.CdmaSmsWriteArgs;
  import android.hardware.radio.V1_0.DataProfileId;
  import android.hardware.radio.V1_0.Dial;
  import android.hardware.radio.V1_0.GsmBroadcastSmsConfigInfo;
  import android.hardware.radio.V1_0.GsmSmsMessage;
  import android.hardware.radio.V1_0.HardwareConfigModem;
  import android.hardware.radio.V1_0.IRadio;
  import android.hardware.radio.V1_0.IccIo;
  import android.hardware.radio.V1_0.ImsSmsMessage;
  import android.hardware.radio.V1_0.IndicationFilter;
  import android.hardware.radio.V1_0.LceDataInfo;
  import android.hardware.radio.V1_0.MvnoType;
  import android.hardware.radio.V1_0.NvWriteItem;
  import android.hardware.radio.V1_0.RadioError;
  import android.hardware.radio.V1_0.RadioIndicationType;
  import android.hardware.radio.V1_0.RadioResponseInfo;
  import android.hardware.radio.V1_0.RadioResponseType;
  import android.hardware.radio.V1_0.ResetNvType;
  import android.hardware.radio.V1_0.SelectUiccSub;
  import android.hardware.radio.V1_0.SimApdu;
  import android.hardware.radio.V1_0.SmsWriteArgs;
  import android.hardware.radio.V1_0.UusInfo;
  import android.hardware.radio.V1_2.AccessNetwork;
  import android.hardware.radio.V1_4.CarrierRestrictionsWithPriority;
  import android.hardware.radio.V1_4.SimLockMultiSimPolicy;
  import android.hardware.radio.deprecated.V1_0.IOemHook;
  import android.net.ConnectivityManager;
  import android.net.KeepalivePacketData;
  import android.net.LinkAddress;
  import android.net.LinkProperties;
  import android.net.NetworkUtils;
  import android.os.AsyncResult;
  import android.os.Build;
  import android.os.Handler;
  import android.os.HwBinder;
  import android.os.Message;
  import android.os.PowerManager;
  import android.os.PowerManager.WakeLock;
  import android.os.RemoteException;
  import android.os.SystemClock;
  import android.os.SystemProperties;
  import android.os.WorkSource;
  import android.provider.Settings;
  import android.service.carrier.CarrierIdentifier;
  import android.telephony.AccessNetworkConstants.AccessNetworkType;
  import android.telephony.CarrierRestrictionRules;
  import android.telephony.CellInfo;
  import android.telephony.CellSignalStrengthCdma;
  import android.telephony.CellSignalStrengthGsm;
  import android.telephony.CellSignalStrengthLte;
  import android.telephony.CellSignalStrengthNr;
  import android.telephony.CellSignalStrengthTdscdma;
  import android.telephony.CellSignalStrengthWcdma;
  import android.telephony.ClientRequestStats;
  import android.telephony.ImsiEncryptionInfo;
  import android.telephony.ModemActivityInfo;
  import android.telephony.NeighboringCellInfo;
  import android.telephony.NetworkScanRequest;
  import android.telephony.PhoneNumberUtils;
  import android.telephony.RadioAccessFamily;
  import android.telephony.RadioAccessSpecifier;
  import android.telephony.Rlog;
  import android.telephony.ServiceState;
  import android.telephony.SignalStrength;
  import android.telephony.SmsManager;
  import android.telephony.TelephonyHistogram;
  import android.telephony.TelephonyManager;
  import android.telephony.TelephonyManager.PrefNetworkMode;
  import android.telephony.data.ApnSetting;
  import android.telephony.data.DataCallResponse;
  import android.telephony.data.DataProfile;
  import android.telephony.data.DataService;
  import android.telephony.emergency.EmergencyNumber;
  import android.text.TextUtils;
  import android.util.Log;
  import android.util.SparseArray;
  
  import com.android.internal.annotations.VisibleForTesting;
  import com.android.internal.telephony.cat.ComprehensionTlv;
  import com.android.internal.telephony.cat.ComprehensionTlvTag;
  import com.android.internal.telephony.cdma.CdmaInformationRecords;
  import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
  import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
  import com.android.internal.telephony.metrics.TelephonyMetrics;
  import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
  import com.android.internal.telephony.uicc.IccUtils;
  
  import java.io.ByteArrayInputStream;
  import java.io.DataInputStream;
  import java.io.FileDescriptor;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.net.Inet4Address;
  import java.net.Inet6Address;
  import java.net.InetAddress;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.List;
  import java.util.NoSuchElementException;
  import java.util.Set;
  import java.util.concurrent.atomic.AtomicBoolean;
  import java.util.concurrent.atomic.AtomicLong;
  
  /**
   * RIL implementation of the CommandsInterface.
   *
   * {@hide}
   */
  public class RIL extends BaseCommands implements CommandsInterface {
      static final String RILJ_LOG_TAG = "RILJ";
      static final String RILJ_WAKELOCK_TAG = "*telephony-radio*";
      // Have a separate wakelock instance for Ack
      static final String RILJ_ACK_WAKELOCK_NAME = "RILJ_ACK_WL";
      static final boolean RILJ_LOGD = true;
      static final boolean RILJ_LOGV = false; // STOPSHIP if true
      static final int RIL_HISTOGRAM_BUCKET_COUNT = 5;
  
      /**
       * Wake lock timeout should be longer than the longest timeout in
       * the vendor ril.
       */
      private static final int DEFAULT_WAKE_LOCK_TIMEOUT_MS = 60000;
  
      // Wake lock default timeout associated with ack
      private static final int DEFAULT_ACK_WAKE_LOCK_TIMEOUT_MS = 200;
  
      private static final int DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS = 2000;
  
      // Variables used to differentiate ack messages from request while calling clearWakeLock()
      public static final int INVALID_WAKELOCK = -1;
      public static final int FOR_WAKELOCK = 0;
      public static final int FOR_ACK_WAKELOCK = 1;
      private final ClientWakelockTracker mClientWakelockTracker = new ClientWakelockTracker();
  
      /** @hide */
      public static final HalVersion RADIO_HAL_VERSION_UNKNOWN = HalVersion.UNKNOWN;
  
      /** @hide */
      public static final HalVersion RADIO_HAL_VERSION_1_0 = new HalVersion(1, 0);
  
      /** @hide */
      public static final HalVersion RADIO_HAL_VERSION_1_1 = new HalVersion(1, 1);
  
      /** @hide */
      public static final HalVersion RADIO_HAL_VERSION_1_2 = new HalVersion(1, 2);
  
      /** @hide */
      public static final HalVersion RADIO_HAL_VERSION_1_3 = new HalVersion(1, 3);
  
      /** @hide */
      public static final HalVersion RADIO_HAL_VERSION_1_4 = new HalVersion(1, 4);
  
      // IRadio version
      private HalVersion mRadioVersion = RADIO_HAL_VERSION_UNKNOWN;
  
      //***** Instance Variables
  
      @UnsupportedAppUsage
      final WakeLock mWakeLock;           // Wake lock associated with request/response
      final WakeLock mAckWakeLock;        // Wake lock associated with ack sent
      final int mWakeLockTimeout;         // Timeout associated with request/response
      final int mAckWakeLockTimeout;      // Timeout associated with ack sent
      // The number of wakelock requests currently active.  Don't release the lock
      // until dec'd to 0
      int mWakeLockCount;
  
      // Variables used to identify releasing of WL on wakelock timeouts
      volatile int mWlSequenceNum = 0;
      volatile int mAckWlSequenceNum = 0;
  
      @UnsupportedAppUsage
      SparseArray<RILRequest> mRequestList = new SparseArray<RILRequest>();
      static SparseArray<TelephonyHistogram> mRilTimeHistograms = new
              SparseArray<TelephonyHistogram>();
  
      Object[] mLastNITZTimeInfo;
  
      // When we are testing emergency calls
      @UnsupportedAppUsage
      AtomicBoolean mTestingEmergencyCall = new AtomicBoolean(false);
  
      final Integer mPhoneId;
  
      /**
       * A set that records if radio service is disabled in hal for
       * a specific phone id slot to avoid further getService request.
       */
      Set<Integer> mDisabledRadioServices = new HashSet();
  
      /**
       * A set that records if oem hook service is disabled in hal for
       * a specific phone id slot to avoid further getService request.
       */
      Set<Integer> mDisabledOemHookServices = new HashSet();
  
      /* default work source which will blame phone process */
      private WorkSource mRILDefaultWorkSource;
  
      /* Worksource containing all applications causing wakelock to be held */
      private WorkSource mActiveWakelockWorkSource;
  
      /** Telephony metrics instance for logging metrics event */
      private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
      /** Radio bug detector instance */
      private RadioBugDetector mRadioBugDetector = null;
  
      boolean mIsMobileNetworkSupported;
      RadioResponse mRadioResponse;
      RadioIndication mRadioIndication;
      volatile IRadio mRadioProxy = null;
      OemHookResponse mOemHookResponse;
      OemHookIndication mOemHookIndication;
      volatile IOemHook mOemHookProxy = null;
      final AtomicLong mRadioProxyCookie = new AtomicLong(0);
      final RadioProxyDeathRecipient mRadioProxyDeathRecipient;
      final RilHandler mRilHandler;
  
      //***** Events
      static final int EVENT_WAKE_LOCK_TIMEOUT    = 2;
      static final int EVENT_ACK_WAKE_LOCK_TIMEOUT    = 4;
      static final int EVENT_BLOCKING_RESPONSE_TIMEOUT = 5;
      static final int EVENT_RADIO_PROXY_DEAD     = 6;
  
      //***** Constants
  
      static final String[] HIDL_SERVICE_NAME = {"slot1", "slot2", "slot3"};
  
      static final int IRADIO_GET_SERVICE_DELAY_MILLIS = 4 * 1000;
  
      static final String EMPTY_ALPHA_LONG = "";
      static final String EMPTY_ALPHA_SHORT = "";
  
      public static List<TelephonyHistogram> getTelephonyRILTimingHistograms() {
          List<TelephonyHistogram> list;
          synchronized (mRilTimeHistograms) {
              list = new ArrayList<>(mRilTimeHistograms.size());
              for (int i = 0; i < mRilTimeHistograms.size(); i++) {
                  TelephonyHistogram entry = new TelephonyHistogram(mRilTimeHistograms.valueAt(i));
                  list.add(entry);
              }
          }
          return list;
      }
  
      /** The handler used to handle the internal event of RIL. */
      @VisibleForTesting
      public class RilHandler extends Handler {
  
          //***** Handler implementation
          @Override
          public void handleMessage(Message msg) {
              RILRequest rr;
  
              switch (msg.what) {
                  case EVENT_WAKE_LOCK_TIMEOUT:
                      // Haven't heard back from the last request.  Assume we're
                      // not getting a response and  release the wake lock.
  
                      // The timer of WAKE_LOCK_TIMEOUT is reset with each
                      // new send request. So when WAKE_LOCK_TIMEOUT occurs
                      // all requests in mRequestList already waited at
                      // least DEFAULT_WAKE_LOCK_TIMEOUT_MS but no response.
                      //
                      // Note: Keep mRequestList so that delayed response
                      // can still be handled when response finally comes.
  
                      synchronized (mRequestList) {
                          if (msg.arg1 == mWlSequenceNum && clearWakeLock(FOR_WAKELOCK)) {
                              if (mRadioBugDetector != null) {
                                  mRadioBugDetector.processWakelockTimeout();
                              }
                              if (RILJ_LOGD) {
                                  int count = mRequestList.size();
                                  Rlog.d(RILJ_LOG_TAG, "WAKE_LOCK_TIMEOUT " +
                                          " mRequestList=" + count);
                                  for (int i = 0; i < count; i++) {
                                      rr = mRequestList.valueAt(i);
                                      Rlog.d(RILJ_LOG_TAG, i + ": [" + rr.mSerial + "] "
                                              + requestToString(rr.mRequest));
                                  }
                              }
                          }
                      }
                      break;
  
                  case EVENT_ACK_WAKE_LOCK_TIMEOUT:
                      if (msg.arg1 == mAckWlSequenceNum && clearWakeLock(FOR_ACK_WAKELOCK)) {
                          if (RILJ_LOGV) {
                              Rlog.d(RILJ_LOG_TAG, "ACK_WAKE_LOCK_TIMEOUT");
                          }
                      }
                      break;
  
                  case EVENT_BLOCKING_RESPONSE_TIMEOUT:
                      int serial = msg.arg1;
                      rr = findAndRemoveRequestFromList(serial);
                      // If the request has already been processed, do nothing
                      if(rr == null) {
                          break;
                      }
  
                      //build a response if expected
                      if (rr.mResult != null) {
                          Object timeoutResponse = getResponseForTimedOutRILRequest(rr);
                          AsyncResult.forMessage( rr.mResult, timeoutResponse, null);
                          rr.mResult.sendToTarget();
                          mMetrics.writeOnRilTimeoutResponse(mPhoneId, rr.mSerial, rr.mRequest);
                      }
  
                      decrementWakeLock(rr);
                      rr.release();
                      break;
  
                  case EVENT_RADIO_PROXY_DEAD:
                      riljLog("handleMessage: EVENT_RADIO_PROXY_DEAD cookie = " + msg.obj +
                              " mRadioProxyCookie = " + mRadioProxyCookie.get());
                      if ((long) msg.obj == mRadioProxyCookie.get()) {
                          resetProxyAndRequestList();
                      }
                      break;
              }
          }
      }
  
      /** Return RadioBugDetector instance for testing. */
      @VisibleForTesting
      public RadioBugDetector getRadioBugDetector() {
          if (mRadioBugDetector == null) {
              mRadioBugDetector = new RadioBugDetector(mContext, mPhoneId);
          }
          return mRadioBugDetector;
      }
  
      /**
       * In order to prevent calls to Telephony from waiting indefinitely
       * low-latency blocking calls will eventually time out. In the event of
       * a timeout, this function generates a response that is returned to the
       * higher layers to unblock the call. This is in lieu of a meaningful
       * response.
       * @param rr The RIL Request that has timed out.
       * @return A default object, such as the one generated by a normal response
       * that is returned to the higher layers.
       **/
      @UnsupportedAppUsage
      private static Object getResponseForTimedOutRILRequest(RILRequest rr) {
          if (rr == null ) return null;
  
          Object timeoutResponse = null;
          switch(rr.mRequest) {
              case RIL_REQUEST_GET_ACTIVITY_INFO:
                  timeoutResponse = new ModemActivityInfo(
                          0, 0, 0, new int [ModemActivityInfo.TX_POWER_LEVELS], 0, 0);
                  break;
          };
          return timeoutResponse;
      }
  
      final class RadioProxyDeathRecipient implements HwBinder.DeathRecipient {
          @Override
          public void serviceDied(long cookie) {
              // Deal with service going away
              riljLog("serviceDied");
              mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, cookie));
          }
      }
  
      private void resetProxyAndRequestList() {
          mRadioProxy = null;
          mOemHookProxy = null;
  
          // increment the cookie so that death notification can be ignored
          mRadioProxyCookie.incrementAndGet();
  
          setRadioState(TelephonyManager.RADIO_POWER_UNAVAILABLE, true /* forceNotifyRegistrants */);
  
          RILRequest.resetSerial();
          // Clear request list on close
          clearRequestList(RADIO_NOT_AVAILABLE, false);
  
          getRadioProxy(null);
          getOemHookProxy(null);
      }
  
      /** Returns a {@link IRadio} instance or null if the service is not available. */
      @VisibleForTesting
      public synchronized IRadio getRadioProxy(Message result) {
          if (!mIsMobileNetworkSupported) {
              if (RILJ_LOGV) riljLog("getRadioProxy: Not calling getService(): wifi-only");
              if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
                  result.sendToTarget();
              }
              return null;
          }
  
          if (mRadioProxy != null) {
              return mRadioProxy;
          }
  
          try {
              if (mDisabledRadioServices.contains(mPhoneId)) {
                  riljLoge("getRadioProxy: mRadioProxy for " + HIDL_SERVICE_NAME[mPhoneId]
                          + " is disabled");
              } else {
                  try {
                      mRadioProxy = android.hardware.radio.V1_4.IRadio.getService(
                              HIDL_SERVICE_NAME[mPhoneId], true);
                      mRadioVersion = RADIO_HAL_VERSION_1_4;
                  } catch (NoSuchElementException e) {
                  }
  
                  if (mRadioProxy == null) {
                      try {
                          mRadioProxy = android.hardware.radio.V1_3.IRadio.getService(
                                  HIDL_SERVICE_NAME[mPhoneId], true);
                          mRadioVersion = RADIO_HAL_VERSION_1_3;
                      } catch (NoSuchElementException e) {
                      }
                  }
  
                  if (mRadioProxy == null) {
                      try {
                          mRadioProxy = android.hardware.radio.V1_2.IRadio.getService(
                                  HIDL_SERVICE_NAME[mPhoneId], true);
                          mRadioVersion = RADIO_HAL_VERSION_1_2;
                      } catch (NoSuchElementException e) {
                      }
                  }
  
                  if (mRadioProxy == null) {
                      try {
                          mRadioProxy = android.hardware.radio.V1_1.IRadio.getService(
                                  HIDL_SERVICE_NAME[mPhoneId], true);
                          mRadioVersion = RADIO_HAL_VERSION_1_1;
                      } catch (NoSuchElementException e) {
                      }
                  }
  
                  if (mRadioProxy == null) {
                      try {
                          mRadioProxy = android.hardware.radio.V1_0.IRadio.getService(
                                  HIDL_SERVICE_NAME[mPhoneId], true);
                          mRadioVersion = RADIO_HAL_VERSION_1_0;
                      } catch (NoSuchElementException e) {
                      }
                  }
  
                  if (mRadioProxy != null) {
                      mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
                              mRadioProxyCookie.incrementAndGet());
                      mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication);
                  } else {
                      mDisabledRadioServices.add(mPhoneId);
                      riljLoge("getRadioProxy: mRadioProxy for "
                              + HIDL_SERVICE_NAME[mPhoneId] + " is disabled");
                  }
              }
          } catch (RemoteException e) {
              mRadioProxy = null;
              riljLoge("RadioProxy getService/setResponseFunctions: " + e);
          }
  
          if (mRadioProxy == null) {
              // getService() is a blocking call, so this should never happen
              riljLoge("getRadioProxy: mRadioProxy == null");
              if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
                  result.sendToTarget();
              }
          }
  
          return mRadioProxy;
      }
  
      /** Returns an {@link IOemHook} instance or null if the service is not available. */
      @VisibleForTesting
      public synchronized IOemHook getOemHookProxy(Message result) {
          if (!mIsMobileNetworkSupported) {
              if (RILJ_LOGV) riljLog("getOemHookProxy: Not calling getService(): wifi-only");
              if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
                  result.sendToTarget();
              }
              return null;
          }
  
          if (mOemHookProxy != null) {
              return mOemHookProxy;
          }
  
          try {
              if (mDisabledOemHookServices.contains(mPhoneId)) {
                  riljLoge("getOemHookProxy: mOemHookProxy for " + HIDL_SERVICE_NAME[mPhoneId]
                          + " is disabled");
              } else {
                  mOemHookProxy = IOemHook.getService(HIDL_SERVICE_NAME[mPhoneId], true);
                  if (mOemHookProxy != null) {
                      // not calling linkToDeath() as ril service runs in the same process and death
                      // notification for that should be sufficient
                      mOemHookProxy.setResponseFunctions(mOemHookResponse, mOemHookIndication);
                  } else {
                      mDisabledOemHookServices.add(mPhoneId);
                      riljLoge("getOemHookProxy: mOemHookProxy for " + HIDL_SERVICE_NAME[mPhoneId]
                              + " is disabled");
                  }
              }
          } catch (NoSuchElementException e) {
              mOemHookProxy = null;
              riljLoge("IOemHook service is not on the device HAL: " + e);
          }  catch (RemoteException e) {
              mOemHookProxy = null;
              riljLoge("OemHookProxy getService/setResponseFunctions: " + e);
          }
  
          if (mOemHookProxy == null) {
              if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
                  result.sendToTarget();
              }
          }
  
          return mOemHookProxy;
      }
  
      //***** Constructors
  
      @UnsupportedAppUsage
      public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
          this(context, preferredNetworkType, cdmaSubscription, null);
      }
  
      @UnsupportedAppUsage
      public RIL(Context context, int preferredNetworkType,
              int cdmaSubscription, Integer instanceId) {
          super(context);
          if (RILJ_LOGD) {
              riljLog("RIL: init preferredNetworkType=" + preferredNetworkType
                      + " cdmaSubscription=" + cdmaSubscription + ")");
          }
  
          mContext = context;
          mCdmaSubscription  = cdmaSubscription;
          mPreferredNetworkType = preferredNetworkType;
          mPhoneType = RILConstants.NO_PHONE;
          mPhoneId = instanceId == null ? 0 : instanceId;
          if (isRadioBugDetectionEnabled()) {
              mRadioBugDetector = new RadioBugDetector(context, mPhoneId);
          }
  
          ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
                  Context.CONNECTIVITY_SERVICE);
          mIsMobileNetworkSupported = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
  
          mRadioResponse = new RadioResponse(this);
          mRadioIndication = new RadioIndication(this);
          mOemHookResponse = new OemHookResponse(this);
          mOemHookIndication = new OemHookIndication(this);
          mRilHandler = new RilHandler();
          mRadioProxyDeathRecipient = new RadioProxyDeathRecipient();
  
          PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
          mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_WAKELOCK_TAG);
          mWakeLock.setReferenceCounted(false);
          mAckWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_ACK_WAKELOCK_NAME);
          mAckWakeLock.setReferenceCounted(false);
          mWakeLockTimeout = SystemProperties.getInt(TelephonyProperties.PROPERTY_WAKE_LOCK_TIMEOUT,
                  DEFAULT_WAKE_LOCK_TIMEOUT_MS);
          mAckWakeLockTimeout = SystemProperties.getInt(
                  TelephonyProperties.PROPERTY_WAKE_LOCK_TIMEOUT, DEFAULT_ACK_WAKE_LOCK_TIMEOUT_MS);
          mWakeLockCount = 0;
          mRILDefaultWorkSource = new WorkSource(context.getApplicationInfo().uid,
                  context.getPackageName());
          mActiveWakelockWorkSource = new WorkSource();
  
          TelephonyDevController tdc = TelephonyDevController.getInstance();
          tdc.registerRIL(this);
  
          // set radio callback; needed to set RadioIndication callback (should be done after
          // wakelock stuff is initialized above as callbacks are received on separate binder threads)
          getRadioProxy(null);
          getOemHookProxy(null);
  
          if (RILJ_LOGD) {
              riljLog("Radio HAL version: " + mRadioVersion);
          }
      }
  
      private boolean isRadioBugDetectionEnabled() {
          return Settings.Global.getInt(
                  mContext.getContentResolver(),
                  Settings.Global.ENABLE_RADIO_BUG_DETECTION,
                  1) != 0;
      }
  
      @Override
      public void setOnNITZTime(Handler h, int what, Object obj) {
          super.setOnNITZTime(h, what, obj);
  
          // Send the last NITZ time if we have it
          if (mLastNITZTimeInfo != null) {
              mNITZTimeRegistrant
                  .notifyRegistrant(
                      new AsyncResult (null, mLastNITZTimeInfo, null));
          }
      }
  
      private void addRequest(RILRequest rr) {
          acquireWakeLock(rr, FOR_WAKELOCK);
          synchronized (mRequestList) {
              rr.mStartTimeMs = SystemClock.elapsedRealtime();
              mRequestList.append(rr.mSerial, rr);
          }
      }
  
      private RILRequest obtainRequest(int request, Message result, WorkSource workSource) {
          RILRequest rr = RILRequest.obtain(request, result, workSource);
          addRequest(rr);
          return rr;
      }
  
      private void handleRadioProxyExceptionForRR(RILRequest rr, String caller, Exception e) {
          riljLoge(caller + ": " + e);
          resetProxyAndRequestList();
      }
  
      private static String convertNullToEmptyString(String string) {
          return string != null ? string : "";
      }
  
      @Override
      public void getIccCardStatus(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_STATUS, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getIccCardStatus(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getIccCardStatus", e);
              }
          }
      }
  
      @Override
      public void getIccSlotsStatus(Message result) {
          if (result != null) {
              AsyncResult.forMessage(result, null,
                      CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
              result.sendToTarget();
          }
      }
  
      @Override
      public void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result) {
          if (result != null) {
              AsyncResult.forMessage(result, null,
                      CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
              result.sendToTarget();
          }
      }
  
      @Override
      public void supplyIccPin(String pin, Message result) {
          supplyIccPinForApp(pin, null, result);
      }
  
      @Override
      public void supplyIccPinForApp(String pin, String aid, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PIN, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " aid = " + aid);
              }
  
              try {
                  radioProxy.supplyIccPinForApp(rr.mSerial,
                          convertNullToEmptyString(pin),
                          convertNullToEmptyString(aid));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "supplyIccPinForApp", e);
              }
          }
      }
  
      @Override
      public void supplyIccPuk(String puk, String newPin, Message result) {
          supplyIccPukForApp(puk, newPin, null, result);
      }
  
      @Override
      public void supplyIccPukForApp(String puk, String newPin, String aid, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PUK, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " aid = " + aid);
              }
  
              try {
                  radioProxy.supplyIccPukForApp(rr.mSerial,
                          convertNullToEmptyString(puk),
                          convertNullToEmptyString(newPin),
                          convertNullToEmptyString(aid));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "supplyIccPukForApp", e);
              }
          }
      }
  
      @Override
      public void supplyIccPin2(String pin, Message result) {
          supplyIccPin2ForApp(pin, null, result);
      }
  
      @Override
      public void supplyIccPin2ForApp(String pin, String aid, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PIN2, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " aid = " + aid);
              }
  
              try {
                  radioProxy.supplyIccPin2ForApp(rr.mSerial,
                          convertNullToEmptyString(pin),
                          convertNullToEmptyString(aid));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "supplyIccPin2ForApp", e);
              }
          }
      }
  
      @Override
      public void supplyIccPuk2(String puk2, String newPin2, Message result) {
          supplyIccPuk2ForApp(puk2, newPin2, null, result);
      }
  
      @Override
      public void supplyIccPuk2ForApp(String puk, String newPin2, String aid, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PUK2, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " aid = " + aid);
              }
  
              try {
                  radioProxy.supplyIccPuk2ForApp(rr.mSerial,
                          convertNullToEmptyString(puk),
                          convertNullToEmptyString(newPin2),
                          convertNullToEmptyString(aid));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "supplyIccPuk2ForApp", e);
              }
          }
      }
  
      @Override
      public void changeIccPin(String oldPin, String newPin, Message result) {
          changeIccPinForApp(oldPin, newPin, null, result);
      }
  
      @Override
      public void changeIccPinForApp(String oldPin, String newPin, String aid, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_SIM_PIN, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " oldPin = "
                          + oldPin + " newPin = " + newPin + " aid = " + aid);
              }
  
              try {
                  radioProxy.changeIccPinForApp(rr.mSerial,
                          convertNullToEmptyString(oldPin),
                          convertNullToEmptyString(newPin),
                          convertNullToEmptyString(aid));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "changeIccPinForApp", e);
              }
          }
      }
  
      @Override
      public void changeIccPin2(String oldPin2, String newPin2, Message result) {
          changeIccPin2ForApp(oldPin2, newPin2, null, result);
      }
  
      @Override
      public void changeIccPin2ForApp(String oldPin2, String newPin2, String aid, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_SIM_PIN2, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " oldPin = "
                          + oldPin2 + " newPin = " + newPin2 + " aid = " + aid);
              }
  
              try {
                  radioProxy.changeIccPin2ForApp(rr.mSerial,
                          convertNullToEmptyString(oldPin2),
                          convertNullToEmptyString(newPin2),
                          convertNullToEmptyString(aid));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "changeIccPin2ForApp", e);
              }
          }
      }
  
      @Override
      public void supplyNetworkDepersonalization(String netpin, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " netpin = "
                          + netpin);
              }
  
              try {
                  radioProxy.supplyNetworkDepersonalization(rr.mSerial,
                          convertNullToEmptyString(netpin));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "supplyNetworkDepersonalization", e);
              }
          }
      }
  
      @Override
      public void getCurrentCalls(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_CURRENT_CALLS, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.getCurrentCalls(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getCurrentCalls", e);
              }
          }
      }
  
      @Override
      public void dial(String address, boolean isEmergencyCall, EmergencyNumber emergencyNumberInfo,
                       boolean hasKnownUserIntentEmergency, int clirMode, Message result) {
          dial(address, isEmergencyCall, emergencyNumberInfo, hasKnownUserIntentEmergency,
                  clirMode, null, result);
      }
  
      @Override
      public void enableModem(boolean enable, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (mRadioVersion.less(RADIO_HAL_VERSION_1_3)) {
              if (RILJ_LOGV) riljLog("enableModem: not supported.");
              if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
                  result.sendToTarget();
              }
              return;
          }
  
          android.hardware.radio.V1_3.IRadio radioProxy13 =
                  (android.hardware.radio.V1_3.IRadio) radioProxy;
          if (radioProxy13 != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_MODEM, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " enable = "
                          + enable);
              }
  
              try {
                  radioProxy13.enableModem(rr.mSerial, enable);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "enableModem", e);
              }
          }
      }
  
      @Override
      public void getModemStatus(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (mRadioVersion.less(RADIO_HAL_VERSION_1_3)) {
              if (RILJ_LOGV) riljLog("getModemStatus: not supported.");
              if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
                  result.sendToTarget();
              }
              return;
          }
  
          android.hardware.radio.V1_3.IRadio radioProxy13 =
                  (android.hardware.radio.V1_3.IRadio) radioProxy;
          if (radioProxy13 != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_MODEM_STATUS, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy13.getModemStackStatus(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getModemStatus", e);
              }
          }
      }
  
      @Override
      public void dial(String address, boolean isEmergencyCall, EmergencyNumber emergencyNumberInfo,
                       boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo,
                       Message result) {
          if (isEmergencyCall && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)
                  && emergencyNumberInfo != null) {
              emergencyDial(address, emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode,
                      uusInfo, result);
              return;
          }
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result,
                      mRILDefaultWorkSource);
  
              Dial dialInfo = new Dial();
              dialInfo.address = convertNullToEmptyString(address);
              dialInfo.clir = clirMode;
              if (uusInfo != null) {
                  UusInfo info = new UusInfo();
                  info.uusType = uusInfo.getType();
                  info.uusDcs = uusInfo.getDcs();
                  info.uusData = new String(uusInfo.getUserData());
                  dialInfo.uusInfo.add(info);
              }
  
              if (RILJ_LOGD) {
                  // Do not log function arg for privacy
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.dial(rr.mSerial, dialInfo);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "dial", e);
              }
          }
      }
  
      private void emergencyDial(String address, EmergencyNumber emergencyNumberInfo,
                                 boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo,
                                 Message result) {
          IRadio radioProxy = getRadioProxy(result);
          // IRadio V1.4
          android.hardware.radio.V1_4.IRadio radioProxy14 =
                  (android.hardware.radio.V1_4.IRadio) radioProxy;
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_EMERGENCY_DIAL, result,
                      mRILDefaultWorkSource);
              Dial dialInfo = new Dial();
              dialInfo.address = convertNullToEmptyString(address);
              dialInfo.clir = clirMode;
              if (uusInfo != null) {
                  UusInfo info = new UusInfo();
                  info.uusType = uusInfo.getType();
                  info.uusDcs = uusInfo.getDcs();
                  info.uusData = new String(uusInfo.getUserData());
                  dialInfo.uusInfo.add(info);
              }
  
              if (RILJ_LOGD) {
                  // Do not log function arg for privacy
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy14.emergencyDial(rr.mSerial, dialInfo,
                          emergencyNumberInfo.getEmergencyServiceCategoryBitmaskInternalDial(),
                          emergencyNumberInfo.getEmergencyUrns() != null
                                  ? new ArrayList(emergencyNumberInfo.getEmergencyUrns())
                                  : new ArrayList<>(),
                          emergencyNumberInfo.getEmergencyCallRouting(),
                          hasKnownUserIntentEmergency,
                          emergencyNumberInfo.getEmergencyNumberSourceBitmask()
                                  == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "emergencyDial", e);
              }
          }
      }
  
      @Override
      public void getIMSI(Message result) {
          getIMSIForApp(null, result);
      }
  
      @Override
      public void getIMSIForApp(String aid, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_IMSI, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString()
                          + ">  " + requestToString(rr.mRequest) + " aid = " + aid);
              }
              try {
                  radioProxy.getImsiForApp(rr.mSerial, convertNullToEmptyString(aid));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getIMSIForApp", e);
              }
          }
      }
  
      @Override
      public void hangupConnection(int gsmIndex, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " gsmIndex = "
                          + gsmIndex);
              }
  
              try {
                  radioProxy.hangup(rr.mSerial, gsmIndex);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "hangupConnection", e);
              }
          }
      }
  
      @UnsupportedAppUsage
      @Override
      public void hangupWaitingOrBackground(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.hangupWaitingOrBackground(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "hangupWaitingOrBackground", e);
              }
          }
      }
  
      @UnsupportedAppUsage
      @Override
      public void hangupForegroundResumeBackground(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.hangupForegroundResumeBackground(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "hangupForegroundResumeBackground", e);
              }
          }
      }
  
      @Override
      public void switchWaitingOrHoldingAndActive(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.switchWaitingOrHoldingAndActive(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "switchWaitingOrHoldingAndActive", e);
              }
          }
      }
  
      @Override
      public void conference(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CONFERENCE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.conference(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "conference", e);
              }
          }
      }
  
      @Override
      public void rejectCall(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_UDUB, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.rejectCall(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "rejectCall", e);
              }
          }
      }
  
      @Override
      public void getLastCallFailCause(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getLastCallFailCause(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getLastCallFailCause", e);
              }
          }
      }
  
      @Override
      public void getSignalStrength(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SIGNAL_STRENGTH, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
                  android.hardware.radio.V1_4.IRadio radioProxy14 =
                          (android.hardware.radio.V1_4.IRadio) radioProxy;
                  try {
                      radioProxy14.getSignalStrength_1_4(rr.mSerial);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "getSignalStrength_1_4", e);
                  }
              } else {
                  try {
                      radioProxy.getSignalStrength(rr.mSerial);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "getSignalStrength", e);
                  }
              }
          }
      }
  
      @Override
      public void getVoiceRegistrationState(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_VOICE_REGISTRATION_STATE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getVoiceRegistrationState(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getVoiceRegistrationState", e);
              }
          }
      }
  
      @Override
      public void getDataRegistrationState(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_DATA_REGISTRATION_STATE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getDataRegistrationState(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getDataRegistrationState", e);
              }
          }
      }
  
      @Override
      public void getOperator(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_OPERATOR, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getOperator(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getOperator", e);
              }
          }
      }
  
      @UnsupportedAppUsage
      @Override
      public void setRadioPower(boolean on, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_RADIO_POWER, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " on = " + on);
              }
  
              try {
                  radioProxy.setRadioPower(rr.mSerial, on);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setRadioPower", e);
              }
          }
      }
  
      @Override
      public void sendDtmf(char c, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_DTMF, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  // Do not log function arg for privacy
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.sendDtmf(rr.mSerial, c + "");
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendDtmf", e);
              }
          }
      }
  
      private GsmSmsMessage constructGsmSendSmsRilRequest(String smscPdu, String pdu) {
          GsmSmsMessage msg = new GsmSmsMessage();
          msg.smscPdu = smscPdu == null ? "" : smscPdu;
          msg.pdu = pdu == null ? "" : pdu;
          return msg;
      }
  
      @Override
      public void sendSMS(String smscPdu, String pdu, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SEND_SMS, result,
                      mRILDefaultWorkSource);
  
              // Do not log function args for privacy
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              GsmSmsMessage msg = constructGsmSendSmsRilRequest(smscPdu, pdu);
  
              try {
                  radioProxy.sendSms(rr.mSerial, msg);
                  mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
                          SmsSession.Event.Format.SMS_FORMAT_3GPP);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendSMS", e);
              }
          }
      }
  
      @Override
      public void sendSMSExpectMore(String smscPdu, String pdu, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SEND_SMS_EXPECT_MORE, result,
                      mRILDefaultWorkSource);
  
              // Do not log function arg for privacy
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              GsmSmsMessage msg = constructGsmSendSmsRilRequest(smscPdu, pdu);
  
              try {
                  radioProxy.sendSMSExpectMore(rr.mSerial, msg);
                  mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
                          SmsSession.Event.Format.SMS_FORMAT_3GPP);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendSMSExpectMore", e);
              }
          }
      }
  
      /**
       * Convert MVNO type string into MvnoType defined in types.hal.
       * @param mvnoType MVNO type
       * @return MVNO type in integer
       */
      private static int convertToHalMvnoType(String mvnoType) {
          switch (mvnoType) {
              case "imsi" : return MvnoType.IMSI;
              case "gid" : return MvnoType.GID;
              case "spn" : return MvnoType.SPN;
              default: return MvnoType.NONE;
          }
      }
  
      /**
       * Convert to DataProfileInfo defined in radio/1.0/types.hal
       * @param dp Data profile
       * @return A converted data profile
       */
      private static android.hardware.radio.V1_0.DataProfileInfo convertToHalDataProfile10(
              DataProfile dp) {
          android.hardware.radio.V1_0.DataProfileInfo dpi =
                  new android.hardware.radio.V1_0.DataProfileInfo();
  
          dpi.profileId = dp.getProfileId();
          dpi.apn = dp.getApn();
          dpi.protocol = ApnSetting.getProtocolStringFromInt(dp.getProtocolType());
          dpi.roamingProtocol = ApnSetting.getProtocolStringFromInt(dp.getRoamingProtocolType());
          dpi.authType = dp.getAuthType();
          dpi.user = dp.getUserName();
          dpi.password = dp.getPassword();
          dpi.type = dp.getType();
          dpi.maxConnsTime = dp.getMaxConnectionsTime();
          dpi.maxConns = dp.getMaxConnections();
          dpi.waitTime = dp.getWaitTime();
          dpi.enabled = dp.isEnabled();
          dpi.supportedApnTypesBitmap = dp.getSupportedApnTypesBitmask();
          // Shift by 1 bit due to the discrepancy between
          // android.hardware.radio.V1_0.RadioAccessFamily and the bitmask version of
          // ServiceState.RIL_RADIO_TECHNOLOGY_XXXX.
          dpi.bearerBitmap = ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
                  dp.getBearerBitmask()) << 1;
          dpi.mtu = dp.getMtu();
          dpi.mvnoType = MvnoType.NONE;
          dpi.mvnoMatchData = "";
  
          return dpi;
      }
  
      /**
       * Convert to DataProfileInfo defined in radio/1.4/types.hal
       * @param dp Data profile
       * @return A converted data profile
       */
      private static android.hardware.radio.V1_4.DataProfileInfo convertToHalDataProfile14(
              DataProfile dp) {
          android.hardware.radio.V1_4.DataProfileInfo dpi =
                  new android.hardware.radio.V1_4.DataProfileInfo();
  
          dpi.apn = dp.getApn();
          dpi.protocol = dp.getProtocolType();
          dpi.roamingProtocol = dp.getRoamingProtocolType();
          dpi.authType = dp.getAuthType();
          dpi.user = dp.getUserName();
          dpi.password = dp.getPassword();
          dpi.type = dp.getType();
          dpi.maxConnsTime = dp.getMaxConnectionsTime();
          dpi.maxConns = dp.getMaxConnections();
          dpi.waitTime = dp.getWaitTime();
          dpi.enabled = dp.isEnabled();
          dpi.supportedApnTypesBitmap = dp.getSupportedApnTypesBitmask();
          // Shift by 1 bit due to the discrepancy between
          // android.hardware.radio.V1_0.RadioAccessFamily and the bitmask version of
          // ServiceState.RIL_RADIO_TECHNOLOGY_XXXX.
          dpi.bearerBitmap = ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
                  dp.getBearerBitmask()) << 1;
          dpi.mtu = dp.getMtu();
          dpi.persistent = dp.isPersistent();
          dpi.preferred = dp.isPreferred();
  
          // profile id is only meaningful when it's persistent on the modem.
          dpi.profileId = (dpi.persistent) ? dp.getProfileId() : DataProfileId.INVALID;
  
          return dpi;
      }
  
      /**
       * Convert NV reset type into ResetNvType defined in types.hal.
       * @param resetType NV reset type.
       * @return Converted reset type in integer or -1 if param is invalid.
       */
      private static int convertToHalResetNvType(int resetType) {
          /**
           * resetType values
           * 1 - reload all NV items
           * 2 - erase NV reset (SCRTN)
           * 3 - factory reset (RTN)
           */
          switch (resetType) {
              case 1: return ResetNvType.RELOAD;
              case 2: return ResetNvType.ERASE;
              case 3: return ResetNvType.FACTORY_RESET;
          }
          return -1;
      }
  
      @Override
      public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
                                boolean allowRoaming, int reason, LinkProperties linkProperties,
                                Message result) {
  
          IRadio radioProxy = getRadioProxy(result);
  
          if (radioProxy != null) {
  
              RILRequest rr = obtainRequest(RIL_REQUEST_SETUP_DATA_CALL, result,
                      mRILDefaultWorkSource);
  
              ArrayList<String> addresses = new ArrayList<>();
              ArrayList<String> dnses = new ArrayList<>();
              if (linkProperties != null) {
                  for (InetAddress address : linkProperties.getAddresses()) {
                      addresses.add(address.getHostAddress());
                  }
                  for (InetAddress dns : linkProperties.getDnsServers()) {
                      dnses.add(dns.getHostAddress());
                  }
              }
  
              try {
                  if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
                      // IRadio V1.4
                      android.hardware.radio.V1_4.IRadio radioProxy14 =
                              (android.hardware.radio.V1_4.IRadio) radioProxy;
  
                      // Convert to HAL data profile
                      android.hardware.radio.V1_4.DataProfileInfo dpi =
                              convertToHalDataProfile14(dataProfile);
  
                      if (RILJ_LOGD) {
                          riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                                  + ",accessNetworkType="
                                  + AccessNetworkType.toString(accessNetworkType) + ",isRoaming="
                                  + isRoaming + ",allowRoaming=" + allowRoaming + "," + dataProfile
                                  + ",addresses=" + addresses + ",dnses=" + dnses);
                      }
  
                      radioProxy14.setupDataCall_1_4(rr.mSerial, accessNetworkType, dpi, allowRoaming,
                              reason, addresses, dnses);
                  } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
                      // IRadio V1.2 and IRadio V1.3
                      android.hardware.radio.V1_2.IRadio radioProxy12 =
                              (android.hardware.radio.V1_2.IRadio) radioProxy;
  
                      // Convert to HAL data profile
                      android.hardware.radio.V1_0.DataProfileInfo dpi =
                              convertToHalDataProfile10(dataProfile);
  
                      if (RILJ_LOGD) {
                          riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                                  + ",accessNetworkType="
                                  + AccessNetworkType.toString(accessNetworkType) + ",isRoaming="
                                  + isRoaming + ",allowRoaming=" + allowRoaming + ","
                                  + dataProfile + ",addresses=" + addresses + ",dnses=" + dnses);
                      }
  
                      radioProxy12.setupDataCall_1_2(rr.mSerial, accessNetworkType, dpi,
                              dataProfile.isPersistent(), allowRoaming, isRoaming, reason,
                              addresses, dnses);
                  } else {
                      // IRadio V1.0 and IRadio V1.1
  
                      // Convert to HAL data profile
                      android.hardware.radio.V1_0.DataProfileInfo dpi =
                              convertToHalDataProfile10(dataProfile);
  
                      // Getting data RAT here is just a workaround to support the older 1.0
                      // vendor RIL. The new data service interface passes access network type
                      // instead of RAT for setup data request. It is impossible to convert access
                      // network type back to RAT here, so we directly get the data RAT from
                      // phone.
                      int dataRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
                      Phone phone = PhoneFactory.getPhone(mPhoneId);
                      if (phone != null) {
                          ServiceState ss = phone.getServiceState();
                          if (ss != null) {
                              dataRat = ss.getRilDataRadioTechnology();
                          }
                      }
                      if (RILJ_LOGD) {
                          riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                                  + ",dataRat=" + dataRat + ",isRoaming=" + isRoaming
                                  + ",allowRoaming=" + allowRoaming + "," + dataProfile);
                      }
  
                      radioProxy.setupDataCall(rr.mSerial, dataRat, dpi,
                              dataProfile.isPersistent(), allowRoaming, isRoaming);
                  }
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setupDataCall", e);
              }
          }
      }
  
      @Override
      public void iccIO(int command, int fileId, String path, int p1, int p2, int p3,
                        String data, String pin2, Message result) {
          iccIOForApp(command, fileId, path, p1, p2, p3, data, pin2, null, result);
      }
  
      @Override
      public void iccIOForApp(int command, int fileId, String path, int p1, int p2, int p3,
                   String data, String pin2, String aid, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SIM_IO, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  if (Build.IS_DEBUGGABLE) {
                      riljLog(rr.serialString() + "> iccIO: "
                              + requestToString(rr.mRequest) + " command = 0x"
                              + Integer.toHexString(command) + " fileId = 0x"
                              + Integer.toHexString(fileId) + " path = " + path + " p1 = "
                              + p1 + " p2 = " + p2 + " p3 = " + " data = " + data
                              + " aid = " + aid);
                  } else {
                      riljLog(rr.serialString() + "> iccIO: " + requestToString(rr.mRequest));
                  }
              }
  
              IccIo iccIo = new IccIo();
              iccIo.command = command;
              iccIo.fileId = fileId;
              iccIo.path = convertNullToEmptyString(path);
              iccIo.p1 = p1;
              iccIo.p2 = p2;
              iccIo.p3 = p3;
              iccIo.data = convertNullToEmptyString(data);
              iccIo.pin2 = convertNullToEmptyString(pin2);
              iccIo.aid = convertNullToEmptyString(aid);
  
              try {
                  radioProxy.iccIOForApp(rr.mSerial, iccIo);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "iccIOForApp", e);
              }
          }
      }
  
      @Override
      public void sendUSSD(String ussd, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SEND_USSD, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  String logUssd = "*******";
                  if (RILJ_LOGV) logUssd = ussd;
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " ussd = " + logUssd);
              }
  
              try {
                  radioProxy.sendUssd(rr.mSerial, convertNullToEmptyString(ussd));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendUSSD", e);
              }
          }
      }
  
      @Override
      public void cancelPendingUssd(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_USSD, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString()
                          + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.cancelPendingUssd(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "cancelPendingUssd", e);
              }
          }
      }
  
      @Override
      public void getCLIR(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_CLIR, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getClir(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getCLIR", e);
              }
          }
      }
  
      @Override
      public void setCLIR(int clirMode, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_CLIR, result, mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " clirMode = " + clirMode);
              }
  
              try {
                  radioProxy.setClir(rr.mSerial, clirMode);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setCLIR", e);
              }
          }
      }
  
      @Override
      public void queryCallForwardStatus(int cfReason, int serviceClass,
                             String number, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " cfreason = " + cfReason + " serviceClass = " + serviceClass);
              }
  
              android.hardware.radio.V1_0.CallForwardInfo cfInfo =
                      new android.hardware.radio.V1_0.CallForwardInfo();
              cfInfo.reason = cfReason;
              cfInfo.serviceClass = serviceClass;
              cfInfo.toa = PhoneNumberUtils.toaFromString(number);
              cfInfo.number = convertNullToEmptyString(number);
              cfInfo.timeSeconds = 0;
  
              try {
                  radioProxy.getCallForwardStatus(rr.mSerial, cfInfo);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "queryCallForwardStatus", e);
              }
          }
      }
  
      @Override
      public void setCallForward(int action, int cfReason, int serviceClass,
                     String number, int timeSeconds, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_CALL_FORWARD, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " action = " + action + " cfReason = " + cfReason + " serviceClass = "
                          + serviceClass + " timeSeconds = " + timeSeconds);
              }
  
              android.hardware.radio.V1_0.CallForwardInfo cfInfo =
                      new android.hardware.radio.V1_0.CallForwardInfo();
              cfInfo.status = action;
              cfInfo.reason = cfReason;
              cfInfo.serviceClass = serviceClass;
              cfInfo.toa = PhoneNumberUtils.toaFromString(number);
              cfInfo.number = convertNullToEmptyString(number);
              cfInfo.timeSeconds = timeSeconds;
  
              try {
                  radioProxy.setCallForward(rr.mSerial, cfInfo);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setCallForward", e);
  
              }
          }
      }
  
      @Override
      public void queryCallWaiting(int serviceClass, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CALL_WAITING, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " serviceClass = " + serviceClass);
              }
  
              try {
                  radioProxy.getCallWaiting(rr.mSerial, serviceClass);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "queryCallWaiting", e);
              }
          }
      }
  
      @Override
      public void setCallWaiting(boolean enable, int serviceClass, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_CALL_WAITING, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " enable = " + enable + " serviceClass = " + serviceClass);
              }
  
              try {
                  radioProxy.setCallWaiting(rr.mSerial, enable, serviceClass);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setCallWaiting", e);
              }
          }
      }
  
      @Override
      public void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SMS_ACKNOWLEDGE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " success = " + success + " cause = " + cause);
              }
  
              try {
                  radioProxy.acknowledgeLastIncomingGsmSms(rr.mSerial, success, cause);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "acknowledgeLastIncomingGsmSms", e);
              }
          }
      }
  
      @Override
      public void acceptCall(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_ANSWER, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.acceptCall(rr.mSerial);
                  mMetrics.writeRilAnswer(mPhoneId, rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "acceptCall", e);
              }
          }
      }
  
      @Override
      public void deactivateDataCall(int cid, int reason, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_DEACTIVATE_DATA_CALL, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> "
                          + requestToString(rr.mRequest) + " cid = " + cid + " reason = " + reason);
              }
  
              try {
                  if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
                      android.hardware.radio.V1_2.IRadio radioProxy12 =
                              (android.hardware.radio.V1_2.IRadio) radioProxy;
  
                      radioProxy12.deactivateDataCall_1_2(rr.mSerial, cid, reason);
                  } else {
                      radioProxy.deactivateDataCall(rr.mSerial, cid,
                              (reason == DataService.REQUEST_REASON_SHUTDOWN));
                  }
                  mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial, cid, reason);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "deactivateDataCall", e);
              }
          }
      }
  
      @Override
      public void queryFacilityLock(String facility, String password, int serviceClass,
                                    Message result) {
          queryFacilityLockForApp(facility, password, serviceClass, null, result);
      }
  
      @Override
      public void queryFacilityLockForApp(String facility, String password, int serviceClass,
                                          String appId, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_FACILITY_LOCK, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " facility = " + facility + " serviceClass = " + serviceClass
                          + " appId = " + appId);
              }
  
              try {
                  radioProxy.getFacilityLockForApp(rr.mSerial,
                          convertNullToEmptyString(facility),
                          convertNullToEmptyString(password),
                          serviceClass,
                          convertNullToEmptyString(appId));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getFacilityLockForApp", e);
              }
          }
      }
  
      @Override
      public void setFacilityLock(String facility, boolean lockState, String password,
                                  int serviceClass, Message result) {
          setFacilityLockForApp(facility, lockState, password, serviceClass, null, result);
      }
  
      @Override
      public void setFacilityLockForApp(String facility, boolean lockState, String password,
                                        int serviceClass, String appId, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_FACILITY_LOCK, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " facility = " + facility + " lockstate = " + lockState
                          + " serviceClass = " + serviceClass + " appId = " + appId);
              }
  
              try {
                  radioProxy.setFacilityLockForApp(rr.mSerial,
                          convertNullToEmptyString(facility),
                          lockState,
                          convertNullToEmptyString(password),
                          serviceClass,
                          convertNullToEmptyString(appId));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setFacilityLockForApp", e);
              }
          }
      }
  
      @Override
      public void changeBarringPassword(String facility, String oldPwd, String newPwd,
                                        Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_BARRING_PASSWORD, result,
                      mRILDefaultWorkSource);
  
              // Do not log all function args for privacy
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + "facility = " + facility);
              }
  
              try {
                  radioProxy.setBarringPassword(rr.mSerial,
                          convertNullToEmptyString(facility),
                          convertNullToEmptyString(oldPwd),
                          convertNullToEmptyString(newPwd));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "changeBarringPassword", e);
              }
          }
      }
  
      @Override
      public void getNetworkSelectionMode(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getNetworkSelectionMode(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getNetworkSelectionMode", e);
              }
          }
      }
  
      @Override
      public void setNetworkSelectionModeAutomatic(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.setNetworkSelectionModeAutomatic(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setNetworkSelectionModeAutomatic", e);
              }
          }
      }
  
      @Override
      public void setNetworkSelectionModeManual(String operatorNumeric, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " operatorNumeric = " + operatorNumeric);
              }
  
              try {
                  radioProxy.setNetworkSelectionModeManual(rr.mSerial,
                          convertNullToEmptyString(operatorNumeric));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setNetworkSelectionModeManual", e);
              }
          }
      }
  
      @Override
      public void getAvailableNetworks(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getAvailableNetworks(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getAvailableNetworks", e);
              }
          }
      }
  
      private android.hardware.radio.V1_1.RadioAccessSpecifier convertRadioAccessSpecifierToRadioHAL(
              RadioAccessSpecifier ras) {
          android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
                  new android.hardware.radio.V1_1.RadioAccessSpecifier();
          rasInHalFormat.radioAccessNetwork = ras.getRadioAccessNetwork();
          List<Integer> bands = null;
          switch (ras.getRadioAccessNetwork()) {
              case AccessNetworkType.GERAN:
                  bands = rasInHalFormat.geranBands;
                  break;
              case AccessNetworkType.UTRAN:
                  bands = rasInHalFormat.utranBands;
                  break;
              case AccessNetworkType.EUTRAN:
                  bands = rasInHalFormat.eutranBands;
                  break;
              default:
                  Log.wtf(RILJ_LOG_TAG, "radioAccessNetwork " + ras.getRadioAccessNetwork()
                          + " not supported!");
                  return null;
          }
  
          if (ras.getBands() != null) {
              for (int band : ras.getBands()) {
                  bands.add(band);
              }
          }
          if (ras.getChannels() != null) {
              for (int channel : ras.getChannels()) {
                  rasInHalFormat.channels.add(channel);
              }
          }
  
          return rasInHalFormat;
      }
  
      @Override
      public void startNetworkScan(NetworkScanRequest nsr, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
  
                  android.hardware.radio.V1_2.NetworkScanRequest request =
                          new android.hardware.radio.V1_2.NetworkScanRequest();
                  request.type = nsr.getScanType();
                  request.interval = nsr.getSearchPeriodicity();
                  request.maxSearchTime = nsr.getMaxSearchTime();
                  request.incrementalResultsPeriodicity = nsr.getIncrementalResultsPeriodicity();
                  request.incrementalResults = nsr.getIncrementalResults();
  
                  for (RadioAccessSpecifier ras : nsr.getSpecifiers()) {
  
                      android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
                              convertRadioAccessSpecifierToRadioHAL(ras);
                      if (rasInHalFormat == null) {
                          return;
                      }
  
                      request.specifiers.add(rasInHalFormat);
                  }
  
                  request.mccMncs.addAll(nsr.getPlmns());
                  RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
                          mRILDefaultWorkSource);
  
                  if (RILJ_LOGD) {
                      riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
                  }
  
                  try {
                      if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
                          android.hardware.radio.V1_4.IRadio radioProxy14 =
                                  (android.hardware.radio.V1_4.IRadio) radioProxy;
                          radioProxy14.startNetworkScan_1_4(rr.mSerial, request);
                      } else {
                          android.hardware.radio.V1_2.IRadio radioProxy12 =
                                  (android.hardware.radio.V1_2.IRadio) radioProxy;
                          radioProxy12.startNetworkScan_1_2(rr.mSerial, request);
                      }
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
                  }
              } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
                  android.hardware.radio.V1_1.IRadio radioProxy11 =
                          (android.hardware.radio.V1_1.IRadio) radioProxy;
  
                  android.hardware.radio.V1_1.NetworkScanRequest request =
                          new android.hardware.radio.V1_1.NetworkScanRequest();
                  request.type = nsr.getScanType();
                  request.interval = nsr.getSearchPeriodicity();
                  for (RadioAccessSpecifier ras : nsr.getSpecifiers()) {
                      android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
                              convertRadioAccessSpecifierToRadioHAL(ras);
                      if (rasInHalFormat == null) {
                          return;
                      }
  
                      request.specifiers.add(rasInHalFormat);
                  }
  
                  RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
                          mRILDefaultWorkSource);
  
                  if (RILJ_LOGD) {
                      riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
                  }
  
                  try {
                      radioProxy11.startNetworkScan(rr.mSerial, request);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
                  }
              } else if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
                  result.sendToTarget();
              }
          }
      }
  
      @Override
      public void stopNetworkScan(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
                  android.hardware.radio.V1_1.IRadio radioProxy11 =
                          (android.hardware.radio.V1_1.IRadio) radioProxy;
  
                  RILRequest rr = obtainRequest(RIL_REQUEST_STOP_NETWORK_SCAN, result,
                          mRILDefaultWorkSource);
  
                  if (RILJ_LOGD) {
                      riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
                  }
  
                  try {
                      radioProxy11.stopNetworkScan(rr.mSerial);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "stopNetworkScan", e);
                  }
              } else if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
                  result.sendToTarget();
              }
          }
      }
  
      @Override
      public void startDtmf(char c, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_DTMF_START, result,
                      mRILDefaultWorkSource);
  
              // Do not log function arg for privacy
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.startDtmf(rr.mSerial, c + "");
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "startDtmf", e);
              }
          }
      }
  
      @Override
      public void stopDtmf(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_DTMF_STOP, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.stopDtmf(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "stopDtmf", e);
              }
          }
      }
  
      @Override
      public void separateConnection(int gsmIndex, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SEPARATE_CONNECTION, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " gsmIndex = " + gsmIndex);
              }
  
              try {
                  radioProxy.separateConnection(rr.mSerial, gsmIndex);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "separateConnection", e);
              }
          }
      }
  
      @Override
      public void getBasebandVersion(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_BASEBAND_VERSION, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getBasebandVersion(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getBasebandVersion", e);
              }
          }
      }
  
      @Override
      public void setMute(boolean enableMute, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_MUTE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " enableMute = " + enableMute);
              }
  
              try {
                  radioProxy.setMute(rr.mSerial, enableMute);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setMute", e);
              }
          }
      }
  
      @Override
      public void getMute(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_MUTE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getMute(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getMute", e);
              }
          }
      }
  
      @Override
      public void queryCLIP(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CLIP, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getClip(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "queryCLIP", e);
              }
          }
      }
  
      /**
       * @deprecated
       */
      @Override
      @Deprecated
      public void getPDPContextList(Message result) {
          getDataCallList(result);
      }
  
      @Override
      public void getDataCallList(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_DATA_CALL_LIST, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getDataCallList(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getDataCallList", e);
              }
          }
      }
  
      @UnsupportedAppUsage
      @Override
      public void invokeOemRilRequestRaw(byte[] data, Message response) {
          IOemHook oemHookProxy = getOemHookProxy(response);
          if (oemHookProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_OEM_HOOK_RAW, response,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + "[" + IccUtils.bytesToHexString(data) + "]");
              }
  
              try {
                  oemHookProxy.sendRequestRaw(rr.mSerial, primitiveArrayToArrayList(data));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestRaw", e);
              }
          } else {
              // OEM Hook service is disabled for P and later devices.
              // Deprecated OEM Hook APIs will perform dummy before being removed.
              if (RILJ_LOGD) riljLog("Radio Oem Hook Service is disabled for P and later devices. ");
          }
      }
  
      @Override
      public void invokeOemRilRequestStrings(String[] strings, Message result) {
          IOemHook oemHookProxy = getOemHookProxy(result);
          if (oemHookProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_OEM_HOOK_STRINGS, result,
                      mRILDefaultWorkSource);
  
              String logStr = "";
              for (int i = 0; i < strings.length; i++) {
                  logStr = logStr + strings[i] + " ";
              }
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " strings = "
                          + logStr);
              }
  
              try {
                  oemHookProxy.sendRequestStrings(rr.mSerial,
                          new ArrayList<String>(Arrays.asList(strings)));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestStrings", e);
              }
          } else {
              // OEM Hook service is disabled for P and later devices.
              // Deprecated OEM Hook APIs will perform dummy before being removed.
              if (RILJ_LOGD) riljLog("Radio Oem Hook Service is disabled for P and later devices. ");
          }
      }
  
      @Override
      public void setSuppServiceNotifications(boolean enable, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " enable = "
                          + enable);
              }
  
              try {
                  radioProxy.setSuppServiceNotifications(rr.mSerial, enable);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setSuppServiceNotifications", e);
              }
          }
      }
  
      @Override
      public void writeSmsToSim(int status, String smsc, String pdu, Message result) {
          status = translateStatus(status);
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_WRITE_SMS_TO_SIM, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGV) {
                  riljLog(rr.serialString() + "> "
                          + requestToString(rr.mRequest)
                          + " " + status);
              }
  
              SmsWriteArgs args = new SmsWriteArgs();
              args.status = status;
              args.smsc = convertNullToEmptyString(smsc);
              args.pdu = convertNullToEmptyString(pdu);
  
              try {
                  radioProxy.writeSmsToSim(rr.mSerial, args);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "writeSmsToSim", e);
              }
          }
      }
  
      @Override
      public void deleteSmsOnSim(int index, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_DELETE_SMS_ON_SIM, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGV) {
                  riljLog(rr.serialString() + "> "
                          + requestToString(rr.mRequest) + " index = " + index);
              }
  
              try {
                  radioProxy.deleteSmsOnSim(rr.mSerial, index);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "deleteSmsOnSim", e);
              }
          }
      }
  
      @Override
      public void setBandMode(int bandMode, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_BAND_MODE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " bandMode = " + bandMode);
              }
  
              try {
                  radioProxy.setBandMode(rr.mSerial, bandMode);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setBandMode", e);
              }
          }
      }
  
      @Override
      public void queryAvailableBandMode(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getAvailableBandModes(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "queryAvailableBandMode", e);
              }
          }
      }
  
      @Override
      public void sendEnvelope(String contents, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " contents = "
                          + contents);
              }
  
              try {
                  radioProxy.sendEnvelope(rr.mSerial, convertNullToEmptyString(contents));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendEnvelope", e);
              }
          }
      }
  
      @Override
      public void sendTerminalResponse(String contents, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " contents = "
                          + (Build.IS_DEBUGGABLE ? contents : censoredTerminalResponse(contents)));
              }
  
              try {
                  radioProxy.sendTerminalResponseToSim(rr.mSerial,
                          convertNullToEmptyString(contents));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendTerminalResponse", e);
              }
          }
      }
  
      private String censoredTerminalResponse(String terminalResponse) {
          try {
              byte[] bytes = IccUtils.hexStringToBytes(terminalResponse);
              if (bytes != null) {
                  List<ComprehensionTlv> ctlvs = ComprehensionTlv.decodeMany(bytes, 0);
                  int from = 0;
                  for (ComprehensionTlv ctlv : ctlvs) {
                      // Find text strings which might be personal information input by user,
                      // then replace it with "********".
                      if (ComprehensionTlvTag.TEXT_STRING.value() == ctlv.getTag()) {
                          byte[] target = Arrays.copyOfRange(ctlv.getRawValue(), from,
                                  ctlv.getValueIndex() + ctlv.getLength());
                          terminalResponse = terminalResponse.toLowerCase().replace(
                                  IccUtils.bytesToHexString(target).toLowerCase(), "********");
                      }
                      // The text string tag and the length field should also be hidden.
                      from = ctlv.getValueIndex() + ctlv.getLength();
                  }
              }
          } catch (Exception e) {
              Rlog.e(RILJ_LOG_TAG, "Could not censor the terminal response: " + e);
              terminalResponse = null;
          }
  
          return terminalResponse;
      }
  
      @Override
      public void sendEnvelopeWithStatus(String contents, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " contents = "
                          + contents);
              }
  
              try {
                  radioProxy.sendEnvelopeWithStatus(rr.mSerial, convertNullToEmptyString(contents));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendEnvelopeWithStatus", e);
              }
          }
      }
  
      @Override
      public void explicitCallTransfer(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_EXPLICIT_CALL_TRANSFER, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.explicitCallTransfer(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "explicitCallTransfer", e);
              }
          }
      }
  
      @Override
      public void setPreferredNetworkType(@PrefNetworkMode int networkType , Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " networkType = " + networkType);
              }
              mPreferredNetworkType = networkType;
              mMetrics.writeSetPreferredNetworkType(mPhoneId, networkType);
  
              if (mRadioVersion.lessOrEqual(RADIO_HAL_VERSION_1_3)) {
                  try {
                      radioProxy.setPreferredNetworkType(rr.mSerial, networkType);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "setPreferredNetworkType", e);
                  }
              } else if (mRadioVersion.equals(RADIO_HAL_VERSION_1_4)) {
                  android.hardware.radio.V1_4.IRadio radioProxy14 =
                          (android.hardware.radio.V1_4.IRadio) radioProxy;
                  try {
                      radioProxy14.setPreferredNetworkTypeBitmap(
                              rr.mSerial, convertToHalRadioAccessFamily(
                                      RadioAccessFamily.getRafFromNetworkType(networkType)));
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "setPreferredNetworkTypeBitmap", e);
                  }
              }
          }
      }
  
      /**
       * convert RAF from {@link android.hardware.radio.V1_0.RadioAccessFamily} to
       * {@link TelephonyManager.NetworkTypeBitMask}, the bitmask represented by
       * {@link TelephonyManager.NetworkType}.
       *
       * @param raf {@link android.hardware.radio.V1_0.RadioAccessFamily}
       * @return {@link TelephonyManager.NetworkTypeBitMask}
       */
      @TelephonyManager.NetworkTypeBitMask
      public static int convertToNetworkTypeBitMask(int raf) {
          int networkTypeRaf = 0;
  
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.GSM) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.GPRS) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EDGE) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EDGE;
          }
          // convert both IS95A/IS95B to CDMA as network mode doesn't support CDMA
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.IS95A) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.IS95B) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.ONE_X_RTT) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_0) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_A) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_B) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EHRPD) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSUPA) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSDPA) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSPA) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSPA;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSPAP) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.UMTS) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.TD_SCDMA) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.LTE) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
          }
          if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.LTE_CA) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
          }
          if ((raf & android.hardware.radio.V1_4.RadioAccessFamily.NR) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_NR;
          }
          // TODO: need hal definition
          if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) != 0) {
              networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN;
          }
          return (networkTypeRaf == 0) ? TelephonyManager.NETWORK_TYPE_UNKNOWN : networkTypeRaf;
      }
  
      // convert to android.hardware.radio.V1_0.RadioAccessFamily
      private static int convertToHalRadioAccessFamily(
              @TelephonyManager.NetworkTypeBitMask int networkTypeBitmask) {
          int raf = 0;
  
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_GSM) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.GSM;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_GPRS) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.GPRS;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_EDGE) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.EDGE;
          }
          // convert CDMA to IS95A, consistent with ServiceState.networkTypeToRilRadioTechnology
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_CDMA) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.IS95A;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.ONE_X_RTT;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.EVDO_0;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.EVDO_A;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.EVDO_B;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.EHRPD;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.HSUPA;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.HSDPA;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_HSPA) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.HSPA;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.HSPAP;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_UMTS) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.UMTS;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.TD_SCDMA;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_LTE) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.LTE;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA) != 0) {
              raf |= android.hardware.radio.V1_0.RadioAccessFamily.LTE_CA;
          }
          if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) != 0) {
              raf |= android.hardware.radio.V1_4.RadioAccessFamily.NR;
          }
          // TODO: need hal definition for IWLAN
          return (raf == 0) ? android.hardware.radio.V1_4.RadioAccessFamily.UNKNOWN : raf;
      }
  
      @Override
      public void getPreferredNetworkType(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, result,
                      mRILDefaultWorkSource);
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              if (mRadioVersion.lessOrEqual(RADIO_HAL_VERSION_1_3)) {
                  try {
                      radioProxy.getPreferredNetworkType(rr.mSerial);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "getPreferredNetworkType", e);
                  }
              } else if (mRadioVersion.equals(RADIO_HAL_VERSION_1_4)) {
                  android.hardware.radio.V1_4.IRadio radioProxy14 =
                          (android.hardware.radio.V1_4.IRadio) radioProxy;
                  try {
                      radioProxy14.getPreferredNetworkTypeBitmap(rr.mSerial);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "getPreferredNetworkTypeBitmap", e);
                  }
              }
          }
      }
  
      @Override
      public void setLocationUpdates(boolean enable, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOCATION_UPDATES, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> "
                          + requestToString(rr.mRequest) + " enable = " + enable);
              }
  
              try {
                  radioProxy.setLocationUpdates(rr.mSerial, enable);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setLocationUpdates", e);
              }
          }
      }
  
      @Override
      public void setCdmaSubscriptionSource(int cdmaSubscription , Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " cdmaSubscription = " + cdmaSubscription);
              }
  
              try {
                  radioProxy.setCdmaSubscriptionSource(rr.mSerial, cdmaSubscription);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setCdmaSubscriptionSource", e);
              }
          }
      }
  
      @Override
      public void queryCdmaRoamingPreference(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getCdmaRoamingPreference(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "queryCdmaRoamingPreference", e);
              }
          }
      }
  
      @Override
      public void setCdmaRoamingPreference(int cdmaRoamingType, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " cdmaRoamingType = " + cdmaRoamingType);
              }
  
              try {
                  radioProxy.setCdmaRoamingPreference(rr.mSerial, cdmaRoamingType);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setCdmaRoamingPreference", e);
              }
          }
      }
  
      @Override
      public void queryTTYMode(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_TTY_MODE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getTTYMode(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "queryTTYMode", e);
              }
          }
      }
  
      @Override
      public void setTTYMode(int ttyMode, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_TTY_MODE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " ttyMode = " + ttyMode);
              }
  
              try {
                  radioProxy.setTTYMode(rr.mSerial, ttyMode);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setTTYMode", e);
              }
          }
      }
  
      @Override
      public void setPreferredVoicePrivacy(boolean enable, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " enable = " + enable);
              }
  
              try {
                  radioProxy.setPreferredVoicePrivacy(rr.mSerial, enable);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setPreferredVoicePrivacy", e);
              }
          }
      }
  
      @Override
      public void getPreferredVoicePrivacy(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE,
                      result, mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getPreferredVoicePrivacy(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getPreferredVoicePrivacy", e);
              }
          }
      }
  
      @Override
      public void sendCDMAFeatureCode(String featureCode, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_FLASH, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " featureCode = " + featureCode);
              }
  
              try {
                  radioProxy.sendCDMAFeatureCode(rr.mSerial, convertNullToEmptyString(featureCode));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendCDMAFeatureCode", e);
              }
          }
      }
  
      @Override
      public void sendBurstDtmf(String dtmfString, int on, int off, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_BURST_DTMF, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " dtmfString = " + dtmfString + " on = " + on + " off = " + off);
              }
  
              try {
                  radioProxy.sendBurstDtmf(rr.mSerial, convertNullToEmptyString(dtmfString), on, off);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendBurstDtmf", e);
              }
          }
      }
  
      private void constructCdmaSendSmsRilRequest(CdmaSmsMessage msg, byte[] pdu) {
          int addrNbrOfDigits;
          int subaddrNbrOfDigits;
          int bearerDataLength;
          ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
          DataInputStream dis = new DataInputStream(bais);
  
          try {
              msg.teleserviceId = dis.readInt(); // teleServiceId
              msg.isServicePresent = (byte) dis.readInt() == 1 ? true : false; // servicePresent
              msg.serviceCategory = dis.readInt(); // serviceCategory
              msg.address.digitMode = dis.read();  // address digit mode
              msg.address.numberMode = dis.read(); // address number mode
              msg.address.numberType = dis.read(); // address number type
              msg.address.numberPlan = dis.read(); // address number plan
              addrNbrOfDigits = (byte) dis.read();
              for (int i = 0; i < addrNbrOfDigits; i++) {
                  msg.address.digits.add(dis.readByte()); // address_orig_bytes[i]
              }
              msg.subAddress.subaddressType = dis.read(); //subaddressType
              msg.subAddress.odd = (byte) dis.read() == 1 ? true : false; //subaddr odd
              subaddrNbrOfDigits = (byte) dis.read();
              for (int i = 0; i < subaddrNbrOfDigits; i++) {
                  msg.subAddress.digits.add(dis.readByte()); //subaddr_orig_bytes[i]
              }
  
              bearerDataLength = dis.read();
              for (int i = 0; i < bearerDataLength; i++) {
                  msg.bearerData.add(dis.readByte()); //bearerData[i]
              }
          } catch (IOException ex) {
              if (RILJ_LOGD) {
                  riljLog("sendSmsCdma: conversion from input stream to object failed: "
                          + ex);
              }
          }
      }
  
      @Override
      public void sendCdmaSms(byte[] pdu, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SEND_SMS, result,
                      mRILDefaultWorkSource);
  
              // Do not log function arg for privacy
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              CdmaSmsMessage msg = new CdmaSmsMessage();
              constructCdmaSendSmsRilRequest(msg, pdu);
  
              try {
                  radioProxy.sendCdmaSms(rr.mSerial, msg);
                  mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
                          SmsSession.Event.Format.SMS_FORMAT_3GPP2);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendCdmaSms", e);
              }
          }
      }
  
      @Override
      public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " success = " + success + " cause = " + cause);
              }
  
              CdmaSmsAck msg = new CdmaSmsAck();
              msg.errorClass = success ? 0 : 1;
              msg.smsCauseCode = cause;
  
              try {
                  radioProxy.acknowledgeLastIncomingCdmaSms(rr.mSerial, msg);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "acknowledgeLastIncomingCdmaSms", e);
              }
          }
      }
  
      @Override
      public void getGsmBroadcastConfig(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GSM_GET_BROADCAST_CONFIG, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getGsmBroadcastConfig(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getGsmBroadcastConfig", e);
              }
          }
      }
  
      @Override
      public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GSM_SET_BROADCAST_CONFIG, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " with " + config.length + " configs : ");
                  for (int i = 0; i < config.length; i++) {
                      riljLog(config[i].toString());
                  }
              }
  
              ArrayList<GsmBroadcastSmsConfigInfo> configs = new ArrayList<>();
  
              int numOfConfig = config.length;
              GsmBroadcastSmsConfigInfo info;
  
              for (int i = 0; i < numOfConfig; i++) {
                  info = new GsmBroadcastSmsConfigInfo();
                  info.fromServiceId = config[i].getFromServiceId();
                  info.toServiceId = config[i].getToServiceId();
                  info.fromCodeScheme = config[i].getFromCodeScheme();
                  info.toCodeScheme = config[i].getToCodeScheme();
                  info.selected = config[i].isSelected();
                  configs.add(info);
              }
  
              try {
                  radioProxy.setGsmBroadcastConfig(rr.mSerial, configs);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setGsmBroadcastConfig", e);
              }
          }
      }
  
      @Override
      public void setGsmBroadcastActivation(boolean activate, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GSM_BROADCAST_ACTIVATION, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " activate = " + activate);
              }
  
              try {
                  radioProxy.setGsmBroadcastActivation(rr.mSerial, activate);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setGsmBroadcastActivation", e);
              }
          }
      }
  
      @Override
      public void getCdmaBroadcastConfig(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getCdmaBroadcastConfig(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getCdmaBroadcastConfig", e);
              }
          }
      }
  
      @Override
      public void setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG, result,
                      mRILDefaultWorkSource);
  
              ArrayList<CdmaBroadcastSmsConfigInfo> halConfigs = new ArrayList<>();
  
              for (CdmaSmsBroadcastConfigInfo config: configs) {
                  for (int i = config.getFromServiceCategory();
                          i <= config.getToServiceCategory();
                          i++) {
                      CdmaBroadcastSmsConfigInfo info = new CdmaBroadcastSmsConfigInfo();
                      info.serviceCategory = i;
                      info.language = config.getLanguage();
                      info.selected = config.isSelected();
                      halConfigs.add(info);
                  }
              }
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " with " + halConfigs.size() + " configs : ");
                  for (CdmaBroadcastSmsConfigInfo config : halConfigs) {
                      riljLog(config.toString());
                  }
              }
  
              try {
                  radioProxy.setCdmaBroadcastConfig(rr.mSerial, halConfigs);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setCdmaBroadcastConfig", e);
              }
          }
      }
  
      @Override
      public void setCdmaBroadcastActivation(boolean activate, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_BROADCAST_ACTIVATION, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " activate = " + activate);
              }
  
              try {
                  radioProxy.setCdmaBroadcastActivation(rr.mSerial, activate);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setCdmaBroadcastActivation", e);
              }
          }
      }
  
      @Override
      public void getCDMASubscription(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SUBSCRIPTION, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getCDMASubscription(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getCDMASubscription", e);
              }
          }
      }
  
      @Override
      public void writeSmsToRuim(int status, String pdu, Message result) {
          status = translateStatus(status);
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGV) {
                  riljLog(rr.serialString() + "> "
                          + requestToString(rr.mRequest)
                          + " status = " + status);
              }
  
              CdmaSmsWriteArgs args = new CdmaSmsWriteArgs();
              args.status = status;
              constructCdmaSendSmsRilRequest(args.message, pdu.getBytes());
  
              try {
                  radioProxy.writeSmsToRuim(rr.mSerial, args);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "writeSmsToRuim", e);
              }
          }
      }
  
      @Override
      public void deleteSmsOnRuim(int index, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGV) {
                  riljLog(rr.serialString() + "> "
                          + requestToString(rr.mRequest)
                          + " index = " + index);
              }
  
              try {
                  radioProxy.deleteSmsOnRuim(rr.mSerial, index);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "deleteSmsOnRuim", e);
              }
          }
      }
  
      @Override
      public void getDeviceIdentity(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_DEVICE_IDENTITY, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getDeviceIdentity(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getDeviceIdentity", e);
              }
          }
      }
  
      @Override
      public void exitEmergencyCallbackMode(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.exitEmergencyCallbackMode(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "exitEmergencyCallbackMode", e);
              }
          }
      }
  
      @Override
      public void getSmscAddress(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_SMSC_ADDRESS, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getSmscAddress(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getSmscAddress", e);
              }
          }
      }
  
      @Override
      public void setSmscAddress(String address, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_SMSC_ADDRESS, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " address = " + address);
              }
  
              try {
                  radioProxy.setSmscAddress(rr.mSerial, convertNullToEmptyString(address));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setSmscAddress", e);
              }
          }
      }
  
      @Override
      public void reportSmsMemoryStatus(boolean available, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> "
                          + requestToString(rr.mRequest) + " available = " + available);
              }
  
              try {
                  radioProxy.reportSmsMemoryStatus(rr.mSerial, available);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "reportSmsMemoryStatus", e);
              }
          }
      }
  
      @Override
      public void reportStkServiceIsRunning(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.reportStkServiceIsRunning(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "reportStkServiceIsRunning", e);
              }
          }
      }
  
      @Override
      public void getCdmaSubscriptionSource(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getCdmaSubscriptionSource(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getCdmaSubscriptionSource", e);
              }
          }
      }
  
      @Override
      public void acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " success = " + success);
              }
  
              try {
                  radioProxy.acknowledgeIncomingGsmSmsWithPdu(rr.mSerial, success,
                          convertNullToEmptyString(ackPdu));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "acknowledgeIncomingGsmSmsWithPdu", e);
              }
          }
      }
  
      @Override
      public void getVoiceRadioTechnology(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_VOICE_RADIO_TECH, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getVoiceRadioTechnology(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getVoiceRadioTechnology", e);
              }
          }
      }
  
      @Override
      public void getCellInfoList(Message result, WorkSource workSource) {
          workSource = getDeafultWorkSourceIfInvalid(workSource);
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_CELL_INFO_LIST, result,
                      workSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.getCellInfoList(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getCellInfoList", e);
              }
          }
      }
  
      @Override
      public void setCellInfoListRate(int rateInMillis, Message result, WorkSource workSource) {
          workSource = getDeafultWorkSourceIfInvalid(workSource);
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE, result,
                      workSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " rateInMillis = " + rateInMillis);
              }
  
              try {
                  radioProxy.setCellInfoListRate(rr.mSerial, rateInMillis);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setCellInfoListRate", e);
              }
          }
      }
  
      @Override
      public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
  
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_INITIAL_ATTACH_APN, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + dataProfile);
              }
  
              try {
                  if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
                      // v1.4
                      android.hardware.radio.V1_4.IRadio radioProxy14 =
                              (android.hardware.radio.V1_4.IRadio) radioProxy;
                      radioProxy14.setInitialAttachApn_1_4(rr.mSerial,
                              convertToHalDataProfile14(dataProfile));
                  } else {
                      // v1.3, v1.2, v1.1, and v1.0
                      radioProxy.setInitialAttachApn(rr.mSerial,
                              convertToHalDataProfile10(dataProfile), dataProfile.isPersistent(),
                              isRoaming);
                  }
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setInitialAttachApn", e);
              }
          }
      }
  
      @Override
      public void getImsRegistrationState(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_IMS_REGISTRATION_STATE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.getImsRegistrationState(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getImsRegistrationState", e);
              }
          }
      }
  
      @Override
      public void sendImsGsmSms(String smscPdu, String pdu, int retry, int messageRef,
                     Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_IMS_SEND_SMS, result,
                      mRILDefaultWorkSource);
  
              // Do not log function args for privacy
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              ImsSmsMessage msg = new ImsSmsMessage();
              msg.tech = RILConstants.GSM_PHONE;
              msg.retry = (byte) retry >= 1 ? true : false;
              msg.messageRef = messageRef;
  
              GsmSmsMessage gsmMsg = constructGsmSendSmsRilRequest(smscPdu, pdu);
              msg.gsmMessage.add(gsmMsg);
              try {
                  radioProxy.sendImsSms(rr.mSerial, msg);
                  mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
                          SmsSession.Event.Format.SMS_FORMAT_3GPP);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendImsGsmSms", e);
              }
          }
      }
  
      @Override
      public void sendImsCdmaSms(byte[] pdu, int retry, int messageRef, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_IMS_SEND_SMS, result,
                      mRILDefaultWorkSource);
  
              // Do not log function args for privacy
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              ImsSmsMessage msg = new ImsSmsMessage();
              msg.tech = RILConstants.CDMA_PHONE;
              msg.retry = (byte) retry >= 1 ? true : false;
              msg.messageRef = messageRef;
  
              CdmaSmsMessage cdmaMsg = new CdmaSmsMessage();
              constructCdmaSendSmsRilRequest(cdmaMsg, pdu);
              msg.cdmaMessage.add(cdmaMsg);
  
              try {
                  radioProxy.sendImsSms(rr.mSerial, msg);
                  mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
                          SmsSession.Event.Format.SMS_FORMAT_3GPP2);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendImsCdmaSms", e);
              }
          }
      }
  
      private SimApdu createSimApdu(int channel, int cla, int instruction, int p1, int p2, int p3,
                                    String data) {
          SimApdu msg = new SimApdu();
          msg.sessionId = channel;
          msg.cla = cla;
          msg.instruction = instruction;
          msg.p1 = p1;
          msg.p2 = p2;
          msg.p3 = p3;
          msg.data = convertNullToEmptyString(data);
          return msg;
      }
  
      @Override
      public void iccTransmitApduBasicChannel(int cla, int instruction, int p1, int p2,
                                              int p3, String data, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  if (Build.IS_DEBUGGABLE) {
                      riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                              + String.format(" cla = 0x%02X ins = 0x%02X", cla, instruction)
                              + String.format(" p1 = 0x%02X p2 = 0x%02X p3 = 0x%02X", p1, p2, p3)
                              + " data = " + data);
                  } else {
                      riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
                  }
              }
  
              SimApdu msg = createSimApdu(0, cla, instruction, p1, p2, p3, data);
              try {
                  radioProxy.iccTransmitApduBasicChannel(rr.mSerial, msg);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "iccTransmitApduBasicChannel", e);
              }
          }
      }
  
      @Override
      public void iccOpenLogicalChannel(String aid, int p2, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SIM_OPEN_CHANNEL, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  if (Build.IS_DEBUGGABLE) {
                      riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " aid = " + aid
                              + " p2 = " + p2);
                  } else {
                      riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
                  }
              }
  
              try {
                  radioProxy.iccOpenLogicalChannel(rr.mSerial, convertNullToEmptyString(aid), p2);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "iccOpenLogicalChannel", e);
              }
          }
      }
  
      @Override
      public void iccCloseLogicalChannel(int channel, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SIM_CLOSE_CHANNEL, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " channel = "
                          + channel);
              }
  
              try {
                  radioProxy.iccCloseLogicalChannel(rr.mSerial, channel);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "iccCloseLogicalChannel", e);
              }
          }
      }
  
      @Override
      public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
                                                int p1, int p2, int p3, String data,
                                                Message result) {
          if (channel <= 0) {
              throw new RuntimeException(
                      "Invalid channel in iccTransmitApduLogicalChannel: " + channel);
          }
  
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  if (Build.IS_DEBUGGABLE) {
                      riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                              + String.format(" channel = %d", channel)
                              + String.format(" cla = 0x%02X ins = 0x%02X", cla, instruction)
                              + String.format(" p1 = 0x%02X p2 = 0x%02X p3 = 0x%02X", p1, p2, p3)
                              + " data = " + data);
                  } else {
                      riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
                  }
              }
  
              SimApdu msg = createSimApdu(channel, cla, instruction, p1, p2, p3, data);
  
              try {
                  radioProxy.iccTransmitApduLogicalChannel(rr.mSerial, msg);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "iccTransmitApduLogicalChannel", e);
              }
          }
      }
  
      @Override
      public void nvReadItem(int itemID, Message result, WorkSource workSource) {
          workSource = getDeafultWorkSourceIfInvalid(workSource);
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_NV_READ_ITEM, result,
                      workSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " itemId = " + itemID);
              }
  
              try {
                  radioProxy.nvReadItem(rr.mSerial, itemID);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "nvReadItem", e);
              }
          }
      }
  
      @Override
      public void nvWriteItem(int itemId, String itemValue, Message result, WorkSource workSource) {
          workSource = getDeafultWorkSourceIfInvalid(workSource);
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_NV_WRITE_ITEM, result,
                      workSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " itemId = " + itemId + " itemValue = " + itemValue);
              }
  
              NvWriteItem item = new NvWriteItem();
              item.itemId = itemId;
              item.value = convertNullToEmptyString(itemValue);
  
              try {
                  radioProxy.nvWriteItem(rr.mSerial, item);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "nvWriteItem", e);
              }
          }
      }
  
      @Override
      public void nvWriteCdmaPrl(byte[] preferredRoamingList, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_NV_WRITE_CDMA_PRL, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " PreferredRoamingList = 0x"
                          + IccUtils.bytesToHexString(preferredRoamingList));
              }
  
              ArrayList<Byte> arrList = new ArrayList<>();
              for (int i = 0; i < preferredRoamingList.length; i++) {
                  arrList.add(preferredRoamingList[i]);
              }
  
              try {
                  radioProxy.nvWriteCdmaPrl(rr.mSerial, arrList);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "nvWriteCdmaPrl", e);
              }
          }
      }
  
      @Override
      public void nvResetConfig(int resetType, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_NV_RESET_CONFIG, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " resetType = " + resetType);
              }
  
              try {
                  radioProxy.nvResetConfig(rr.mSerial, convertToHalResetNvType(resetType));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "nvResetConfig", e);
              }
          }
      }
  
      @Override
      public void setUiccSubscription(int slotId, int appIndex, int subId,
                                      int subStatus, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_UICC_SUBSCRIPTION, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " slot = " + slotId + " appIndex = " + appIndex
                          + " subId = " + subId + " subStatus = " + subStatus);
              }
  
              SelectUiccSub info = new SelectUiccSub();
              info.slot = slotId;
              info.appIndex = appIndex;
              info.subType = subId;
              info.actStatus = subStatus;
  
              try {
                  radioProxy.setUiccSubscription(rr.mSerial, info);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setUiccSubscription", e);
              }
          }
      }
  
      @Override
      public void setDataAllowed(boolean allowed, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_ALLOW_DATA, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " allowed = " + allowed);
              }
  
              try {
                  radioProxy.setDataAllowed(rr.mSerial, allowed);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setDataAllowed", e);
              }
          }
      }
  
      @Override
      public void getHardwareConfig(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_HARDWARE_CONFIG, result,
                      mRILDefaultWorkSource);
  
              // Do not log function args for privacy
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.getHardwareConfig(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getHardwareConfig", e);
              }
          }
      }
  
      @Override
      public void requestIccSimAuthentication(int authContext, String data, String aid,
                                              Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SIM_AUTHENTICATION, result,
                      mRILDefaultWorkSource);
  
              // Do not log function args for privacy
              if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
              try {
                  radioProxy.requestIccSimAuthentication(rr.mSerial,
                          authContext,
                          convertNullToEmptyString(data),
                          convertNullToEmptyString(aid));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "requestIccSimAuthentication", e);
              }
          }
      }
  
      @Override
      public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
  
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
  
              RILRequest rr = null;
              try {
                  if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
                      // V1.4
                      android.hardware.radio.V1_4.IRadio radioProxy14 =
                              (android.hardware.radio.V1_4.IRadio) radioProxy;
  
                      rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
                              mRILDefaultWorkSource);
  
                      ArrayList<android.hardware.radio.V1_4.DataProfileInfo> dpis = new ArrayList<>();
                      for (DataProfile dp : dps) {
                          dpis.add(convertToHalDataProfile14(dp));
                      }
  
                      if (RILJ_LOGD) {
                          riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                                  + " with data profiles : ");
                          for (DataProfile profile : dps) {
                              riljLog(profile.toString());
                          }
                      }
  
                      radioProxy14.setDataProfile_1_4(rr.mSerial, dpis);
                  } else {
                      // V1.0, 1.1, 1,2 and 1.3
                      ArrayList<android.hardware.radio.V1_0.DataProfileInfo> dpis = new ArrayList<>();
                      for (DataProfile dp : dps) {
                          // For v1.0 to v1.2, we only send data profiles that has the persistent
                          // (a.k.a modem cognitive) bit set to true.
                          if (dp.isPersistent()) {
                              dpis.add(convertToHalDataProfile10(dp));
                          }
                      }
  
                      if (!dpis.isEmpty()) {
                          rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
                                  mRILDefaultWorkSource);
  
                          if (RILJ_LOGD) {
                              riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                                      + " with data profiles : ");
                              for (DataProfile profile : dps) {
                                  riljLog(profile.toString());
                              }
                          }
  
                          radioProxy.setDataProfile(rr.mSerial, dpis, isRoaming);
                      }
                  }
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setDataProfile", e);
              }
          }
      }
  
      @Override
      public void requestShutdown(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SHUTDOWN, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.requestShutdown(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "requestShutdown", e);
              }
          }
      }
  
      @Override
      public void getRadioCapability(Message response) {
          IRadio radioProxy = getRadioProxy(response);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_RADIO_CAPABILITY, response,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.getRadioCapability(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getRadioCapability", e);
              }
          }
      }
  
      @Override
      public void setRadioCapability(RadioCapability rc, Message response) {
          IRadio radioProxy = getRadioProxy(response);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_RADIO_CAPABILITY, response,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " RadioCapability = " + rc.toString());
              }
  
              android.hardware.radio.V1_0.RadioCapability halRc =
                      new android.hardware.radio.V1_0.RadioCapability();
  
              halRc.session = rc.getSession();
              halRc.phase = rc.getPhase();
              halRc.raf = rc.getRadioAccessFamily();
              halRc.logicalModemUuid = convertNullToEmptyString(rc.getLogicalModemUuid());
              halRc.status = rc.getStatus();
  
              try {
                  radioProxy.setRadioCapability(rr.mSerial, halRc);
              } catch (Exception e) {
                  handleRadioProxyExceptionForRR(rr, "setRadioCapability", e);
              }
          }
      }
  
      @Override
      public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
          IRadio radioProxy = getRadioProxy(result);
  
          if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
              // We have a 1.2 or later radio, so the LCE 1.0 LCE service control path is unused.
              // Instead the LCE functionality is always-on and provides unsolicited indications.
              return;
          }
  
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_START_LCE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                          + " reportIntervalMs = " + reportIntervalMs + " pullMode = " + pullMode);
              }
  
              try {
                  radioProxy.startLceService(rr.mSerial, reportIntervalMs, pullMode);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "startLceService", e);
              }
          }
      }
  
      @Override
      public void stopLceService(Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
              // We have a 1.2 or later radio, so the LCE 1.0 LCE service control is unused.
              // Instead the LCE functionality is always-on and provides unsolicited indications.
              return;
          }
  
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_STOP_LCE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.stopLceService(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "stopLceService", e);
              }
          }
      }
  
      /**
       * This will only be called if the LCE service is started in PULL mode, which is
       * only enabled when using Radio HAL versions 1.1 and earlier.
       *
       * It is still possible for vendors to override this behavior and use the 1.1 version
       * of LCE; however, this is strongly discouraged and this functionality will be removed
       * when HAL 1.x support is dropped.
       *
       * @deprecated HAL 1.2 and later use an always-on LCE that relies on indications.
       */
      @Deprecated
      @Override
      public void pullLceData(Message response) {
          IRadio radioProxy = getRadioProxy(response);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_PULL_LCEDATA, response,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.pullLceData(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "pullLceData", e);
              }
          }
      }
  
      @Override
      public void getModemActivityInfo(Message result, WorkSource workSource) {
          workSource = getDeafultWorkSourceIfInvalid(workSource);
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_GET_ACTIVITY_INFO, result,
                      workSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.getModemActivityInfo(rr.mSerial);
  
                  Message msg = mRilHandler.obtainMessage(EVENT_BLOCKING_RESPONSE_TIMEOUT);
                  msg.obj = null;
                  msg.arg1 = rr.mSerial;
                  mRilHandler.sendMessageDelayed(msg, DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getModemActivityInfo", e);
              }
          }
  
  
      }
  
      /**
       * Convert a list of CarrierIdentifier into a list of Carrier defined in 1.0/types.hal.
       * @param carriers List of CarrierIdentifier
       * @return List of converted objects
       */
      @VisibleForTesting
      public static ArrayList<Carrier> createCarrierRestrictionList(
              List<CarrierIdentifier> carriers) {
          ArrayList<Carrier> result = new ArrayList<>();
          for (CarrierIdentifier ci : carriers) {
              Carrier c = new Carrier();
              c.mcc = convertNullToEmptyString(ci.getMcc());
              c.mnc = convertNullToEmptyString(ci.getMnc());
              int matchType = CarrierIdentifier.MatchType.ALL;
              String matchData = null;
              if (!TextUtils.isEmpty(ci.getSpn())) {
                  matchType = CarrierIdentifier.MatchType.SPN;
                  matchData = ci.getSpn();
              } else if (!TextUtils.isEmpty(ci.getImsi())) {
                  matchType = CarrierIdentifier.MatchType.IMSI_PREFIX;
                  matchData = ci.getImsi();
              } else if (!TextUtils.isEmpty(ci.getGid1())) {
                  matchType = CarrierIdentifier.MatchType.GID1;
                  matchData = ci.getGid1();
              } else if (!TextUtils.isEmpty(ci.getGid2())) {
                  matchType = CarrierIdentifier.MatchType.GID2;
                  matchData = ci.getGid2();
              }
              c.matchType = matchType;
              c.matchData = convertNullToEmptyString(matchData);
              result.add(c);
          }
          return result;
      }
  
      @Override
      public void setAllowedCarriers(CarrierRestrictionRules carrierRestrictionRules,
              Message result, WorkSource workSource) {
          riljLog("RIL.java - setAllowedCarriers");
  
          checkNotNull(carrierRestrictionRules, "Carrier restriction cannot be null.");
          workSource = getDeafultWorkSourceIfInvalid(workSource);
  
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy == null) {
              return;
          }
  
          RILRequest rr = obtainRequest(RIL_REQUEST_SET_ALLOWED_CARRIERS, result, workSource);
  
          if (RILJ_LOGD) {
              riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " params: "
                      + carrierRestrictionRules);
          }
  
          // Extract multisim policy
          int policy = SimLockMultiSimPolicy.NO_MULTISIM_POLICY;
          switch (carrierRestrictionRules.getMultiSimPolicy()) {
              case CarrierRestrictionRules.MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT:
                  policy = SimLockMultiSimPolicy.ONE_VALID_SIM_MUST_BE_PRESENT;
                  break;
          }
  
          if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
              riljLog("RIL.java - Using IRadio 1.4 or greater");
  
              android.hardware.radio.V1_4.IRadio radioProxy14 =
                      (android.hardware.radio.V1_4.IRadio) radioProxy;
  
              // Prepare structure with allowed list, excluded list and priority
              CarrierRestrictionsWithPriority carrierRestrictions =
                      new CarrierRestrictionsWithPriority();
              carrierRestrictions.allowedCarriers =
                      createCarrierRestrictionList(carrierRestrictionRules.getAllowedCarriers());
              carrierRestrictions.excludedCarriers =
                      createCarrierRestrictionList(carrierRestrictionRules.getExcludedCarriers());
              carrierRestrictions.allowedCarriersPrioritized =
                      (carrierRestrictionRules.getDefaultCarrierRestriction()
                          == CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED);
  
              try {
                  radioProxy14.setAllowedCarriers_1_4(rr.mSerial, carrierRestrictions, policy);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setAllowedCarriers_1_4", e);
              }
          } else {
              boolean isAllCarriersAllowed = carrierRestrictionRules.isAllCarriersAllowed();
  
              boolean supported = (isAllCarriersAllowed
                      || (carrierRestrictionRules.getExcludedCarriers().isEmpty()
                          && (carrierRestrictionRules.getDefaultCarrierRestriction()
                              == CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED)));
              supported = supported && (policy == SimLockMultiSimPolicy.NO_MULTISIM_POLICY);
  
              if (!supported) {
                  // Feature is not supported by IRadio interface
                  riljLoge("setAllowedCarriers does not support excluded list on IRadio version"
                          + " less than 1.4");
                  if (result != null) {
                      AsyncResult.forMessage(result, null,
                              CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
                      result.sendToTarget();
                  }
                  return;
              }
              riljLog("RIL.java - Using IRadio 1.3 or lower");
  
              // Prepare structure with allowed list
              CarrierRestrictions carrierRestrictions = new CarrierRestrictions();
              carrierRestrictions.allowedCarriers =
                      createCarrierRestrictionList(carrierRestrictionRules.getAllowedCarriers());
  
              try {
                  radioProxy.setAllowedCarriers(rr.mSerial, isAllCarriersAllowed,
                          carrierRestrictions);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setAllowedCarriers", e);
              }
          }
      }
  
      @Override
      public void getAllowedCarriers(Message result, WorkSource workSource) {
          workSource = getDeafultWorkSourceIfInvalid(workSource);
  
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy == null) {
              return;
          }
  
          RILRequest rr = obtainRequest(RIL_REQUEST_GET_ALLOWED_CARRIERS, result,
                  workSource);
  
          if (RILJ_LOGD) {
              riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
          }
  
          if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
              riljLog("RIL.java - Using IRadio 1.4 or greater");
  
              android.hardware.radio.V1_4.IRadio radioProxy14 =
                      (android.hardware.radio.V1_4.IRadio) radioProxy;
  
              try {
                  radioProxy14.getAllowedCarriers_1_4(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getAllowedCarriers_1_4", e);
              }
          } else {
              riljLog("RIL.java - Using IRadio 1.3 or lower");
  
              try {
                  radioProxy.getAllowedCarriers(rr.mSerial);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getAllowedCarriers", e);
              }
          }
      }
  
      @Override
      public void sendDeviceState(int stateType, boolean state,
                                  Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SEND_DEVICE_STATE, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " "
                          + stateType + ":" + state);
              }
  
              try {
                  radioProxy.sendDeviceState(rr.mSerial, stateType, state);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendDeviceState", e);
              }
          }
      }
  
      @Override
      public void setUnsolResponseFilter(int filter, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER, result,
                      mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + filter);
              }
  
              if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
                  try {
                      android.hardware.radio.V1_2.IRadio radioProxy12 =
                              (android.hardware.radio.V1_2.IRadio) radioProxy;
  
                      radioProxy12.setIndicationFilter_1_2(rr.mSerial, filter);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "setIndicationFilter_1_2", e);
                  }
              } else {
                  try {
                      int filter10 = filter & IndicationFilter.ALL;
                      radioProxy.setIndicationFilter(rr.mSerial, filter10);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "setIndicationFilter", e);
                  }
              }
          }
      }
  
      @Override
      public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
              int[] thresholdsDbm, int ran, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              if (mRadioVersion.less(RADIO_HAL_VERSION_1_2)) {
                  riljLoge("setSignalStrengthReportingCriteria ignored on IRadio version less "
                          + "than 1.2");
                  return;
              }
  
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA,
                      result, mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  android.hardware.radio.V1_2.IRadio radioProxy12 =
                          (android.hardware.radio.V1_2.IRadio) radioProxy;
                  radioProxy12.setSignalStrengthReportingCriteria(rr.mSerial, hysteresisMs,
                          hysteresisDb, primitiveArrayToArrayList(thresholdsDbm),
                          convertRanToHalRan(ran));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setSignalStrengthReportingCriteria", e);
              }
          }
      }
  
      @Override
      public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
              int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
              Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              if (mRadioVersion.less(RADIO_HAL_VERSION_1_2)) {
                  riljLoge("setLinkCapacityReportingCriteria ignored on IRadio version less "
                          + "than 1.2");
                  return;
              }
  
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA,
                      result, mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  android.hardware.radio.V1_2.IRadio radioProxy12 =
                          (android.hardware.radio.V1_2.IRadio) radioProxy;
                  radioProxy12.setLinkCapacityReportingCriteria(rr.mSerial, hysteresisMs,
                          hysteresisDlKbps, hysteresisUlKbps,
                          primitiveArrayToArrayList(thresholdsDlKbps),
                          primitiveArrayToArrayList(thresholdsUlKbps), convertRanToHalRan(ran));
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "setLinkCapacityReportingCriteria", e);
              }
          }
      }
  
      private static int convertRanToHalRan(int radioAccessNetwork) {
          switch (radioAccessNetwork) {
              case AccessNetworkType.GERAN:
                  return AccessNetwork.GERAN;
              case AccessNetworkType.UTRAN:
                  return AccessNetwork.UTRAN;
              case AccessNetworkType.EUTRAN:
                  return AccessNetwork.EUTRAN;
              case AccessNetworkType.CDMA2000:
                  return AccessNetwork.CDMA2000;
              case AccessNetworkType.IWLAN:
                  return AccessNetwork.IWLAN;
              case AccessNetworkType.UNKNOWN:
              default:
                  return 0;
          }
      }
  
      @Override
      public void setSimCardPower(int state, Message result, WorkSource workSource) {
          workSource = getDeafultWorkSourceIfInvalid(workSource);
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIM_CARD_POWER, result,
                      workSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + state);
              }
  
              if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
                  try {
                      android.hardware.radio.V1_1.IRadio radioProxy11 =
                              (android.hardware.radio.V1_1.IRadio) radioProxy;
                      radioProxy11.setSimCardPower_1_1(rr.mSerial, state);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "setSimCardPower", e);
                  }
              } else {
                  try {
                      switch (state) {
                          case TelephonyManager.CARD_POWER_DOWN: {
                              radioProxy.setSimCardPower(rr.mSerial, false);
                              break;
                          }
                          case TelephonyManager.CARD_POWER_UP: {
                              radioProxy.setSimCardPower(rr.mSerial, true);
                              break;
                          }
                          default: {
                              if (result != null) {
                                  AsyncResult.forMessage(result, null,
                                          CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
                                  result.sendToTarget();
                              }
                          }
                      }
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "setSimCardPower", e);
                  }
              }
          }
      }
  
      @Override
      public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
                                                  Message result) {
          checkNotNull(imsiEncryptionInfo, "ImsiEncryptionInfo cannot be null.");
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
                  android.hardware.radio.V1_1.IRadio radioProxy11 =
                          (android.hardware.radio.V1_1.IRadio ) radioProxy;
  
                  RILRequest rr = obtainRequest(RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION, result,
                          mRILDefaultWorkSource);
                  if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
                  try {
                      android.hardware.radio.V1_1.ImsiEncryptionInfo halImsiInfo =
                              new android.hardware.radio.V1_1.ImsiEncryptionInfo();
                      halImsiInfo.mnc = imsiEncryptionInfo.getMnc();
                      halImsiInfo.mcc = imsiEncryptionInfo.getMcc();
                      halImsiInfo.keyIdentifier = imsiEncryptionInfo.getKeyIdentifier();
                      if (imsiEncryptionInfo.getExpirationTime() != null) {
                          halImsiInfo.expirationTime =
                                  imsiEncryptionInfo.getExpirationTime().getTime();
                      }
                      for (byte b : imsiEncryptionInfo.getPublicKey().getEncoded()) {
                          halImsiInfo.carrierKey.add(new Byte(b));
                      }
  
                      radioProxy11.setCarrierInfoForImsiEncryption(
                              rr.mSerial, halImsiInfo);
                  } catch (RemoteException | RuntimeException e) {
                      handleRadioProxyExceptionForRR(rr, "setCarrierInfoForImsiEncryption", e);
                  }
              } else if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
                  result.sendToTarget();
              }
          }
      }
  
      @Override
      public void startNattKeepalive(
              int contextId, KeepalivePacketData packetData, int intervalMillis, Message result) {
          checkNotNull(packetData, "KeepaliveRequest cannot be null.");
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy == null) {
              riljLoge("Radio Proxy object is null!");
              return;
          }
  
          if (mRadioVersion.less(RADIO_HAL_VERSION_1_1)) {
              if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
                  result.sendToTarget();
              }
              return;
          }
  
          android.hardware.radio.V1_1.IRadio radioProxy11 =
                  (android.hardware.radio.V1_1.IRadio) radioProxy;
  
          RILRequest rr = obtainRequest(
                  RIL_REQUEST_START_KEEPALIVE, result, mRILDefaultWorkSource);
  
          if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
          try {
              android.hardware.radio.V1_1.KeepaliveRequest req =
                      new android.hardware.radio.V1_1.KeepaliveRequest();
  
              req.cid = contextId;
  
              if (packetData.dstAddress instanceof Inet4Address) {
                  req.type = android.hardware.radio.V1_1.KeepaliveType.NATT_IPV4;
              } else if (packetData.dstAddress instanceof Inet6Address) {
                  req.type = android.hardware.radio.V1_1.KeepaliveType.NATT_IPV6;
              } else {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(INVALID_ARGUMENTS));
                  result.sendToTarget();
                  return;
              }
  
              appendPrimitiveArrayToArrayList(
                      packetData.srcAddress.getAddress(), req.sourceAddress);
              req.sourcePort = packetData.srcPort;
              appendPrimitiveArrayToArrayList(
                      packetData.dstAddress.getAddress(), req.destinationAddress);
              req.destinationPort = packetData.dstPort;
              req.maxKeepaliveIntervalMillis = intervalMillis;
  
              radioProxy11.startKeepalive(rr.mSerial, req);
          } catch (RemoteException | RuntimeException e) {
              handleRadioProxyExceptionForRR(rr, "startNattKeepalive", e);
          }
      }
  
      @Override
      public void stopNattKeepalive(int sessionHandle, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy == null) {
              Rlog.e(RIL.RILJ_LOG_TAG, "Radio Proxy object is null!");
              return;
          }
  
          if (mRadioVersion.less(RADIO_HAL_VERSION_1_1)) {
              if (result != null) {
                  AsyncResult.forMessage(result, null,
                          CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
                  result.sendToTarget();
              }
              return;
          }
  
          android.hardware.radio.V1_1.IRadio radioProxy11 =
                  (android.hardware.radio.V1_1.IRadio) radioProxy;
  
          RILRequest rr = obtainRequest(
                  RIL_REQUEST_STOP_KEEPALIVE, result, mRILDefaultWorkSource);
  
          if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  
          try {
              radioProxy11.stopKeepalive(rr.mSerial, sessionHandle);
          } catch (RemoteException | RuntimeException e) {
              handleRadioProxyExceptionForRR(rr, "stopNattKeepalive", e);
          }
      }
  
      @Override
      public void getIMEI(Message result) {
          throw new RuntimeException("getIMEI not expected to be called");
      }
  
      @Override
      public void getIMEISV(Message result) {
          throw new RuntimeException("getIMEISV not expected to be called");
      }
  
      /**
       * @deprecated
       */
      @Deprecated
      @Override
      public void getLastPdpFailCause(Message result) {
          throw new RuntimeException("getLastPdpFailCause not expected to be called");
      }
  
      /**
       * The preferred new alternative to getLastPdpFailCause
       */
      @Override
      public void getLastDataCallFailCause(Message result) {
          throw new RuntimeException("getLastDataCallFailCause not expected to be called");
      }
  
      /**
       *  Translates EF_SMS status bits to a status value compatible with
       *  SMS AT commands.  See TS 27.005 3.1.
       */
      private int translateStatus(int status) {
          switch(status & 0x7) {
              case SmsManager.STATUS_ON_ICC_READ:
                  return 1;
              case SmsManager.STATUS_ON_ICC_UNREAD:
                  return 0;
              case SmsManager.STATUS_ON_ICC_SENT:
                  return 3;
              case SmsManager.STATUS_ON_ICC_UNSENT:
                  return 2;
          }
  
          // Default to READ.
          return 1;
      }
  
      @Override
      public void resetRadio(Message result) {
          throw new RuntimeException("resetRadio not expected to be called");
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleCallSetupRequestFromSim(boolean accept, Message result) {
          IRadio radioProxy = getRadioProxy(result);
          if (radioProxy != null) {
              RILRequest rr = obtainRequest(RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM,
                      result, mRILDefaultWorkSource);
  
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
              }
  
              try {
                  radioProxy.handleStkCallSetupRequestFromSim(rr.mSerial, accept);
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "getAllowedCarriers", e);
              }
          }
      }
  
      //***** Private Methods
  
      /**
       * This is a helper function to be called when a RadioIndication callback is called.
       * It takes care of acquiring wakelock and sending ack if needed.
       * @param indicationType RadioIndicationType received
       */
      void processIndication(int indicationType) {
          if (indicationType == RadioIndicationType.UNSOLICITED_ACK_EXP) {
              sendAck();
              if (RILJ_LOGD) riljLog("Unsol response received; Sending ack to ril.cpp");
          } else {
              // ack is not expected to be sent back. Nothing is required to be done here.
          }
      }
  
      void processRequestAck(int serial) {
          RILRequest rr;
          synchronized (mRequestList) {
              rr = mRequestList.get(serial);
          }
          if (rr == null) {
              Rlog.w(RIL.RILJ_LOG_TAG, "processRequestAck: Unexpected solicited ack response! "
                      + "serial: " + serial);
          } else {
              decrementWakeLock(rr);
              if (RIL.RILJ_LOGD) {
                  riljLog(rr.serialString() + " Ack < " + RIL.requestToString(rr.mRequest));
              }
          }
      }
  
      /**
       * This is a helper function to be called when a RadioResponse callback is called.
       * It takes care of acks, wakelocks, and finds and returns RILRequest corresponding to the
       * response if one is found.
       * @param responseInfo RadioResponseInfo received in response callback
       * @return RILRequest corresponding to the response
       */
      @VisibleForTesting
      public RILRequest processResponse(RadioResponseInfo responseInfo) {
          int serial = responseInfo.serial;
          int error = responseInfo.error;
          int type = responseInfo.type;
  
          RILRequest rr = null;
  
          if (type == RadioResponseType.SOLICITED_ACK) {
              synchronized (mRequestList) {
                  rr = mRequestList.get(serial);
              }
              if (rr == null) {
                  Rlog.w(RILJ_LOG_TAG, "Unexpected solicited ack response! sn: " + serial);
              } else {
                  decrementWakeLock(rr);
                  if (mRadioBugDetector != null) {
                      mRadioBugDetector.detectRadioBug(rr.mRequest, error);
                  }
                  if (RILJ_LOGD) {
                      riljLog(rr.serialString() + " Ack < " + requestToString(rr.mRequest));
                  }
              }
              return rr;
          }
  
          rr = findAndRemoveRequestFromList(serial);
          if (rr == null) {
              Rlog.e(RIL.RILJ_LOG_TAG, "processResponse: Unexpected response! serial: " + serial
                      + " error: " + error);
              return null;
          }
  
          // Time logging for RIL command and storing it in TelephonyHistogram.
          addToRilHistogram(rr);
          if (mRadioBugDetector != null) {
              mRadioBugDetector.detectRadioBug(rr.mRequest, error);
          }
          if (type == RadioResponseType.SOLICITED_ACK_EXP) {
              sendAck();
              if (RIL.RILJ_LOGD) {
                  riljLog("Response received for " + rr.serialString() + " "
                          + RIL.requestToString(rr.mRequest) + " Sending ack to ril.cpp");
              }
          } else {
              // ack sent for SOLICITED_ACK_EXP above; nothing to do for SOLICITED response
          }
  
          // Here and below fake RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, see b/7255789.
          // This is needed otherwise we don't automatically transition to the main lock
          // screen when the pin or puk is entered incorrectly.
          switch (rr.mRequest) {
              case RIL_REQUEST_ENTER_SIM_PUK:
              case RIL_REQUEST_ENTER_SIM_PUK2:
                  if (mIccStatusChangedRegistrants != null) {
                      if (RILJ_LOGD) {
                          riljLog("ON enter sim puk fakeSimStatusChanged: reg count="
                                  + mIccStatusChangedRegistrants.size());
                      }
                      mIccStatusChangedRegistrants.notifyRegistrants();
                  }
                  break;
              case RIL_REQUEST_SHUTDOWN:
                  setRadioState(TelephonyManager.RADIO_POWER_UNAVAILABLE,
                          false /* forceNotifyRegistrants */);
                  break;
          }
  
          if (error != RadioError.NONE) {
              switch (rr.mRequest) {
                  case RIL_REQUEST_ENTER_SIM_PIN:
                  case RIL_REQUEST_ENTER_SIM_PIN2:
                  case RIL_REQUEST_CHANGE_SIM_PIN:
                  case RIL_REQUEST_CHANGE_SIM_PIN2:
                  case RIL_REQUEST_SET_FACILITY_LOCK:
                      if (mIccStatusChangedRegistrants != null) {
                          if (RILJ_LOGD) {
                              riljLog("ON some errors fakeSimStatusChanged: reg count="
                                      + mIccStatusChangedRegistrants.size());
                          }
                          mIccStatusChangedRegistrants.notifyRegistrants();
                      }
                      break;
  
              }
          } else {
              switch (rr.mRequest) {
                  case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
                  if (mTestingEmergencyCall.getAndSet(false)) {
                      if (mEmergencyCallbackModeRegistrant != null) {
                          riljLog("testing emergency call, notify ECM Registrants");
                          mEmergencyCallbackModeRegistrant.notifyRegistrant();
                      }
                  }
              }
          }
          return rr;
      }
  
      /**
       * This is a helper function to be called at the end of all RadioResponse callbacks.
       * It takes care of sending error response, logging, decrementing wakelock if needed, and
       * releases the request from memory pool.
       * @param rr RILRequest for which response callback was called
       * @param responseInfo RadioResponseInfo received in the callback
       * @param ret object to be returned to request sender
       */
      @VisibleForTesting
      public void processResponseDone(RILRequest rr, RadioResponseInfo responseInfo, Object ret) {
          if (responseInfo.error == 0) {
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
                          + " " + retToString(rr.mRequest, ret));
              }
          } else {
              if (RILJ_LOGD) {
                  riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
                          + " error " + responseInfo.error);
              }
              rr.onError(responseInfo.error, ret);
          }
          mMetrics.writeOnRilSolicitedResponse(mPhoneId, rr.mSerial, responseInfo.error,
                  rr.mRequest, ret);
          if (rr != null) {
              if (responseInfo.type == RadioResponseType.SOLICITED) {
                  decrementWakeLock(rr);
              }
              rr.release();
          }
      }
  
      /**
       * Function to send ack and acquire related wakelock
       */
      private void sendAck() {
          // TODO: Remove rr and clean up acquireWakelock for response and ack
          RILRequest rr = RILRequest.obtain(RIL_RESPONSE_ACKNOWLEDGEMENT, null,
                  mRILDefaultWorkSource);
          acquireWakeLock(rr, RIL.FOR_ACK_WAKELOCK);
          IRadio radioProxy = getRadioProxy(null);
          if (radioProxy != null) {
              try {
                  radioProxy.responseAcknowledgement();
              } catch (RemoteException | RuntimeException e) {
                  handleRadioProxyExceptionForRR(rr, "sendAck", e);
                  riljLoge("sendAck: " + e);
              }
          } else {
              Rlog.e(RILJ_LOG_TAG, "Error trying to send ack, radioProxy = null");
          }
          rr.release();
      }
  
      private WorkSource getDeafultWorkSourceIfInvalid(WorkSource workSource) {
          if (workSource == null) {
              workSource = mRILDefaultWorkSource;
          }
  
          return workSource;
      }
  
  
      /**
       * Holds a PARTIAL_WAKE_LOCK whenever
       * a) There is outstanding RIL request sent to RIL deamon and no replied
       * b) There is a request pending to be sent out.
       *
       * There is a WAKE_LOCK_TIMEOUT to release the lock, though it shouldn't
       * happen often.
       */
      @UnsupportedAppUsage
      private void acquireWakeLock(RILRequest rr, int wakeLockType) {
          synchronized (rr) {
              if (rr.mWakeLockType != INVALID_WAKELOCK) {
                  Rlog.d(RILJ_LOG_TAG, "Failed to aquire wakelock for " + rr.serialString());
                  return;
              }
  
              switch(wakeLockType) {
                  case FOR_WAKELOCK:
                      synchronized (mWakeLock) {
                          mWakeLock.acquire();
                          mWakeLockCount++;
                          mWlSequenceNum++;
  
                          String clientId = rr.getWorkSourceClientId();
                          if (!mClientWakelockTracker.isClientActive(clientId)) {
                              mActiveWakelockWorkSource.add(rr.mWorkSource);
                              mWakeLock.setWorkSource(mActiveWakelockWorkSource);
                          }
  
                          mClientWakelockTracker.startTracking(rr.mClientId,
                                  rr.mRequest, rr.mSerial, mWakeLockCount);
  
                          Message msg = mRilHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
                          msg.arg1 = mWlSequenceNum;
                          mRilHandler.sendMessageDelayed(msg, mWakeLockTimeout);
                      }
                      break;
                  case FOR_ACK_WAKELOCK:
                      synchronized (mAckWakeLock) {
                          mAckWakeLock.acquire();
                          mAckWlSequenceNum++;
  
                          Message msg = mRilHandler.obtainMessage(EVENT_ACK_WAKE_LOCK_TIMEOUT);
                          msg.arg1 = mAckWlSequenceNum;
                          mRilHandler.sendMessageDelayed(msg, mAckWakeLockTimeout);
                      }
                      break;
                  default: //WTF
                      Rlog.w(RILJ_LOG_TAG, "Acquiring Invalid Wakelock type " + wakeLockType);
                      return;
              }
              rr.mWakeLockType = wakeLockType;
          }
      }
  
      /** Returns the wake lock of the given type. */
      @VisibleForTesting
      public WakeLock getWakeLock(int wakeLockType) {
          return wakeLockType == FOR_WAKELOCK ? mWakeLock : mAckWakeLock;
      }
  
      /** Returns the {@link RilHandler} instance. */
      @VisibleForTesting
      public RilHandler getRilHandler() {
          return mRilHandler;
      }
  
      /** Returns the Ril request list. */
      @VisibleForTesting
      public SparseArray<RILRequest> getRilRequestList() {
          return mRequestList;
      }
  
      @UnsupportedAppUsage
      private void decrementWakeLock(RILRequest rr) {
          synchronized (rr) {
              switch(rr.mWakeLockType) {
                  case FOR_WAKELOCK:
                      synchronized (mWakeLock) {
                          mClientWakelockTracker.stopTracking(rr.mClientId,
                                  rr.mRequest, rr.mSerial,
                                  (mWakeLockCount > 1) ? mWakeLockCount - 1 : 0);
                          String clientId = rr.getWorkSourceClientId();
                          if (!mClientWakelockTracker.isClientActive(clientId)) {
                              mActiveWakelockWorkSource.remove(rr.mWorkSource);
                              mWakeLock.setWorkSource(mActiveWakelockWorkSource);
                          }
  
                          if (mWakeLockCount > 1) {
                              mWakeLockCount--;
                          } else {
                              mWakeLockCount = 0;
                              mWakeLock.release();
                          }
                      }
                      break;
                  case FOR_ACK_WAKELOCK:
                      //We do not decrement the ACK wakelock
                      break;
                  case INVALID_WAKELOCK:
                      break;
                  default:
                      Rlog.w(RILJ_LOG_TAG, "Decrementing Invalid Wakelock type " + rr.mWakeLockType);
              }
              rr.mWakeLockType = INVALID_WAKELOCK;
          }
      }
  
      @UnsupportedAppUsage
      private boolean clearWakeLock(int wakeLockType) {
          if (wakeLockType == FOR_WAKELOCK) {
              synchronized (mWakeLock) {
                  if (mWakeLockCount == 0 && !mWakeLock.isHeld()) return false;
                  Rlog.d(RILJ_LOG_TAG, "NOTE: mWakeLockCount is " + mWakeLockCount
                          + "at time of clearing");
                  mWakeLockCount = 0;
                  mWakeLock.release();
                  mClientWakelockTracker.stopTrackingAll();
                  mActiveWakelockWorkSource = new WorkSource();
                  return true;
              }
          } else {
              synchronized (mAckWakeLock) {
                  if (!mAckWakeLock.isHeld()) return false;
                  mAckWakeLock.release();
                  return true;
              }
          }
      }
  
      /**
       * Release each request in mRequestList then clear the list
       * @param error is the RIL_Errno sent back
       * @param loggable true means to print all requests in mRequestList
       */
      @UnsupportedAppUsage
      private void clearRequestList(int error, boolean loggable) {
          RILRequest rr;
          synchronized (mRequestList) {
              int count = mRequestList.size();
              if (RILJ_LOGD && loggable) {
                  Rlog.d(RILJ_LOG_TAG, "clearRequestList " + " mWakeLockCount="
                          + mWakeLockCount + " mRequestList=" + count);
              }
  
              for (int i = 0; i < count; i++) {
                  rr = mRequestList.valueAt(i);
                  if (RILJ_LOGD && loggable) {
                      Rlog.d(RILJ_LOG_TAG, i + ": [" + rr.mSerial + "] "
                              + requestToString(rr.mRequest));
                  }
                  rr.onError(error, null);
                  decrementWakeLock(rr);
                  rr.release();
              }
              mRequestList.clear();
          }
      }
  
      @UnsupportedAppUsage
      private RILRequest findAndRemoveRequestFromList(int serial) {
          RILRequest rr = null;
          synchronized (mRequestList) {
              rr = mRequestList.get(serial);
              if (rr != null) {
                  mRequestList.remove(serial);
              }
          }
  
          return rr;
      }
  
      private void addToRilHistogram(RILRequest rr) {
          long endTime = SystemClock.elapsedRealtime();
          int totalTime = (int) (endTime - rr.mStartTimeMs);
  
          synchronized (mRilTimeHistograms) {
              TelephonyHistogram entry = mRilTimeHistograms.get(rr.mRequest);
              if (entry == null) {
                  // We would have total #RIL_HISTOGRAM_BUCKET_COUNT range buckets for RIL commands
                  entry = new TelephonyHistogram(TelephonyHistogram.TELEPHONY_CATEGORY_RIL,
                          rr.mRequest, RIL_HISTOGRAM_BUCKET_COUNT);
                  mRilTimeHistograms.put(rr.mRequest, entry);
              }
              entry.addTimeTaken(totalTime);
          }
      }
  
      @UnsupportedAppUsage
      RadioCapability makeStaticRadioCapability() {
          // default to UNKNOWN so we fail fast.
          int raf = RadioAccessFamily.RAF_UNKNOWN;
  
          String rafString = mContext.getResources().getString(
                  com.android.internal.R.string.config_radio_access_family);
          if (!TextUtils.isEmpty(rafString)) {
              raf = RadioAccessFamily.rafTypeFromString(rafString);
          }
          RadioCapability rc = new RadioCapability(mPhoneId.intValue(), 0, 0, raf,
                  "", RadioCapability.RC_STATUS_SUCCESS);
          if (RILJ_LOGD) riljLog("Faking RIL_REQUEST_GET_RADIO_CAPABILITY response using " + raf);
          return rc;
      }
  
      @UnsupportedAppUsage
      static String retToString(int req, Object ret) {
          if (ret == null) return "";
          switch (req) {
              // Don't log these return values, for privacy's sake.
              case RIL_REQUEST_GET_IMSI:
              case RIL_REQUEST_GET_IMEI:
              case RIL_REQUEST_GET_IMEISV:
              case RIL_REQUEST_SIM_OPEN_CHANNEL:
              case RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL:
  
                  if (!RILJ_LOGV) {
                      // If not versbose logging just return and don't display IMSI and IMEI, IMEISV
                      return "";
                  }
          }
  
          StringBuilder sb;
          String s;
          int length;
          if (ret instanceof int[]) {
              int[] intArray = (int[]) ret;
              length = intArray.length;
              sb = new StringBuilder("{");
              if (length > 0) {
                  int i = 0;
                  sb.append(intArray[i++]);
                  while (i < length) {
                      sb.append(", ").append(intArray[i++]);
                  }
              }
              sb.append("}");
              s = sb.toString();
          } else if (ret instanceof String[]) {
              String[] strings = (String[]) ret;
              length = strings.length;
              sb = new StringBuilder("{");
              if (length > 0) {
                  int i = 0;
                  // position 0 is IMEI in RIL_REQUEST_DEVICE_IDENTITY
                  if (req == RIL_REQUEST_DEVICE_IDENTITY) {
                      sb.append(Rlog.pii(RILJ_LOG_TAG, strings[i++]));
                  } else {
                      sb.append(strings[i++]);
                  }
                  while (i < length) {
                      sb.append(", ").append(strings[i++]);
                  }
              }
              sb.append("}");
              s = sb.toString();
          } else if (req == RIL_REQUEST_GET_CURRENT_CALLS) {
              ArrayList<DriverCall> calls = (ArrayList<DriverCall>) ret;
              sb = new StringBuilder("{");
              for (DriverCall dc : calls) {
                  sb.append("[").append(dc).append("] ");
              }
              sb.append("}");
              s = sb.toString();
          } else if (req == RIL_REQUEST_GET_NEIGHBORING_CELL_IDS) {
              ArrayList<NeighboringCellInfo> cells = (ArrayList<NeighboringCellInfo>) ret;
              sb = new StringBuilder("{");
              for (NeighboringCellInfo cell : cells) {
                  sb.append("[").append(cell).append("] ");
              }
              sb.append("}");
              s = sb.toString();
          } else if (req == RIL_REQUEST_QUERY_CALL_FORWARD_STATUS) {
              CallForwardInfo[] cinfo = (CallForwardInfo[]) ret;
              length = cinfo.length;
              sb = new StringBuilder("{");
              for (int i = 0; i < length; i++) {
                  sb.append("[").append(cinfo[i]).append("] ");
              }
              sb.append("}");
              s = sb.toString();
          } else if (req == RIL_REQUEST_GET_HARDWARE_CONFIG) {
              ArrayList<HardwareConfig> hwcfgs = (ArrayList<HardwareConfig>) ret;
              sb = new StringBuilder(" ");
              for (HardwareConfig hwcfg : hwcfgs) {
                  sb.append("[").append(hwcfg).append("] ");
              }
              s = sb.toString();
          } else {
              s = ret.toString();
          }
          return s;
      }
  
      void writeMetricsCallRing(char[] response) {
          mMetrics.writeRilCallRing(mPhoneId, response);
      }
  
      void writeMetricsSrvcc(int state) {
          mMetrics.writeRilSrvcc(mPhoneId, state);
      }
  
      void writeMetricsModemRestartEvent(String reason) {
          mMetrics.writeModemRestartEvent(mPhoneId, reason);
      }
  
      /**
       * Notify all registrants that the ril has connected or disconnected.
       *
       * @param rilVer is the version of the ril or -1 if disconnected.
       */
      @UnsupportedAppUsage
      void notifyRegistrantsRilConnectionChanged(int rilVer) {
          mRilVersion = rilVer;
          if (mRilConnectedRegistrants != null) {
              mRilConnectedRegistrants.notifyRegistrants(
                      new AsyncResult(null, new Integer(rilVer), null));
          }
      }
  
      @UnsupportedAppUsage
      void
      notifyRegistrantsCdmaInfoRec(CdmaInformationRecords infoRec) {
          int response = RIL_UNSOL_CDMA_INFO_REC;
          if (infoRec.record instanceof CdmaInformationRecords.CdmaDisplayInfoRec) {
              if (mDisplayInfoRegistrants != null) {
                  if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                  mDisplayInfoRegistrants.notifyRegistrants(
                          new AsyncResult(null, infoRec.record, null));
              }
          } else if (infoRec.record instanceof CdmaInformationRecords.CdmaSignalInfoRec) {
              if (mSignalInfoRegistrants != null) {
                  if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                  mSignalInfoRegistrants.notifyRegistrants(
                          new AsyncResult(null, infoRec.record, null));
              }
          } else if (infoRec.record instanceof CdmaInformationRecords.CdmaNumberInfoRec) {
              if (mNumberInfoRegistrants != null) {
                  if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                  mNumberInfoRegistrants.notifyRegistrants(
                          new AsyncResult(null, infoRec.record, null));
              }
          } else if (infoRec.record instanceof CdmaInformationRecords.CdmaRedirectingNumberInfoRec) {
              if (mRedirNumInfoRegistrants != null) {
                  if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                  mRedirNumInfoRegistrants.notifyRegistrants(
                          new AsyncResult(null, infoRec.record, null));
              }
          } else if (infoRec.record instanceof CdmaInformationRecords.CdmaLineControlInfoRec) {
              if (mLineControlInfoRegistrants != null) {
                  if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                  mLineControlInfoRegistrants.notifyRegistrants(
                          new AsyncResult(null, infoRec.record, null));
              }
          } else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53ClirInfoRec) {
              if (mT53ClirInfoRegistrants != null) {
                  if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                  mT53ClirInfoRegistrants.notifyRegistrants(
                          new AsyncResult(null, infoRec.record, null));
              }
          } else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53AudioControlInfoRec) {
              if (mT53AudCntrlInfoRegistrants != null) {
                  if (RILJ_LOGD) {
                      unsljLogRet(response, infoRec.record);
                  }
                  mT53AudCntrlInfoRegistrants.notifyRegistrants(
                          new AsyncResult(null, infoRec.record, null));
              }
          }
      }
  
      @UnsupportedAppUsage
      static String requestToString(int request) {
          switch(request) {
              case RIL_REQUEST_GET_SIM_STATUS:
                  return "GET_SIM_STATUS";
              case RIL_REQUEST_ENTER_SIM_PIN:
                  return "ENTER_SIM_PIN";
              case RIL_REQUEST_ENTER_SIM_PUK:
                  return "ENTER_SIM_PUK";
              case RIL_REQUEST_ENTER_SIM_PIN2:
                  return "ENTER_SIM_PIN2";
              case RIL_REQUEST_ENTER_SIM_PUK2:
                  return "ENTER_SIM_PUK2";
              case RIL_REQUEST_CHANGE_SIM_PIN:
                  return "CHANGE_SIM_PIN";
              case RIL_REQUEST_CHANGE_SIM_PIN2:
                  return "CHANGE_SIM_PIN2";
              case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION:
                  return "ENTER_NETWORK_DEPERSONALIZATION";
              case RIL_REQUEST_GET_CURRENT_CALLS:
                  return "GET_CURRENT_CALLS";
              case RIL_REQUEST_DIAL:
                  return "DIAL";
              case RIL_REQUEST_EMERGENCY_DIAL:
                  return "EMERGENCY_DIAL";
              case RIL_REQUEST_GET_IMSI:
                  return "GET_IMSI";
              case RIL_REQUEST_HANGUP:
                  return "HANGUP";
              case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
                  return "HANGUP_WAITING_OR_BACKGROUND";
              case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
                  return "HANGUP_FOREGROUND_RESUME_BACKGROUND";
              case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE:
                  return "REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE";
              case RIL_REQUEST_CONFERENCE:
                  return "CONFERENCE";
              case RIL_REQUEST_UDUB:
                  return "UDUB";
              case RIL_REQUEST_LAST_CALL_FAIL_CAUSE:
                  return "LAST_CALL_FAIL_CAUSE";
              case RIL_REQUEST_SIGNAL_STRENGTH:
                  return "SIGNAL_STRENGTH";
              case RIL_REQUEST_VOICE_REGISTRATION_STATE:
                  return "VOICE_REGISTRATION_STATE";
              case RIL_REQUEST_DATA_REGISTRATION_STATE:
                  return "DATA_REGISTRATION_STATE";
              case RIL_REQUEST_OPERATOR:
                  return "OPERATOR";
              case RIL_REQUEST_RADIO_POWER:
                  return "RADIO_POWER";
              case RIL_REQUEST_DTMF:
                  return "DTMF";
              case RIL_REQUEST_SEND_SMS:
                  return "SEND_SMS";
              case RIL_REQUEST_SEND_SMS_EXPECT_MORE:
                  return "SEND_SMS_EXPECT_MORE";
              case RIL_REQUEST_SETUP_DATA_CALL:
                  return "SETUP_DATA_CALL";
              case RIL_REQUEST_SIM_IO:
                  return "SIM_IO";
              case RIL_REQUEST_SEND_USSD:
                  return "SEND_USSD";
              case RIL_REQUEST_CANCEL_USSD:
                  return "CANCEL_USSD";
              case RIL_REQUEST_GET_CLIR:
                  return "GET_CLIR";
              case RIL_REQUEST_SET_CLIR:
                  return "SET_CLIR";
              case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS:
                  return "QUERY_CALL_FORWARD_STATUS";
              case RIL_REQUEST_SET_CALL_FORWARD:
                  return "SET_CALL_FORWARD";
              case RIL_REQUEST_QUERY_CALL_WAITING:
                  return "QUERY_CALL_WAITING";
              case RIL_REQUEST_SET_CALL_WAITING:
                  return "SET_CALL_WAITING";
              case RIL_REQUEST_SMS_ACKNOWLEDGE:
                  return "SMS_ACKNOWLEDGE";
              case RIL_REQUEST_GET_IMEI:
                  return "GET_IMEI";
              case RIL_REQUEST_GET_IMEISV:
                  return "GET_IMEISV";
              case RIL_REQUEST_ANSWER:
                  return "ANSWER";
              case RIL_REQUEST_DEACTIVATE_DATA_CALL:
                  return "DEACTIVATE_DATA_CALL";
              case RIL_REQUEST_QUERY_FACILITY_LOCK:
                  return "QUERY_FACILITY_LOCK";
              case RIL_REQUEST_SET_FACILITY_LOCK:
                  return "SET_FACILITY_LOCK";
              case RIL_REQUEST_CHANGE_BARRING_PASSWORD:
                  return "CHANGE_BARRING_PASSWORD";
              case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE:
                  return "QUERY_NETWORK_SELECTION_MODE";
              case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC:
                  return "SET_NETWORK_SELECTION_AUTOMATIC";
              case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL:
                  return "SET_NETWORK_SELECTION_MANUAL";
              case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS :
                  return "QUERY_AVAILABLE_NETWORKS ";
              case RIL_REQUEST_DTMF_START:
                  return "DTMF_START";
              case RIL_REQUEST_DTMF_STOP:
                  return "DTMF_STOP";
              case RIL_REQUEST_BASEBAND_VERSION:
                  return "BASEBAND_VERSION";
              case RIL_REQUEST_SEPARATE_CONNECTION:
                  return "SEPARATE_CONNECTION";
              case RIL_REQUEST_SET_MUTE:
                  return "SET_MUTE";
              case RIL_REQUEST_GET_MUTE:
                  return "GET_MUTE";
              case RIL_REQUEST_QUERY_CLIP:
                  return "QUERY_CLIP";
              case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE:
                  return "LAST_DATA_CALL_FAIL_CAUSE";
              case RIL_REQUEST_DATA_CALL_LIST:
                  return "DATA_CALL_LIST";
              case RIL_REQUEST_RESET_RADIO:
                  return "RESET_RADIO";
              case RIL_REQUEST_OEM_HOOK_RAW:
                  return "OEM_HOOK_RAW";
              case RIL_REQUEST_OEM_HOOK_STRINGS:
                  return "OEM_HOOK_STRINGS";
              case RIL_REQUEST_SCREEN_STATE:
                  return "SCREEN_STATE";
              case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION:
                  return "SET_SUPP_SVC_NOTIFICATION";
              case RIL_REQUEST_WRITE_SMS_TO_SIM:
                  return "WRITE_SMS_TO_SIM";
              case RIL_REQUEST_DELETE_SMS_ON_SIM:
                  return "DELETE_SMS_ON_SIM";
              case RIL_REQUEST_SET_BAND_MODE:
                  return "SET_BAND_MODE";
              case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE:
                  return "QUERY_AVAILABLE_BAND_MODE";
              case RIL_REQUEST_STK_GET_PROFILE:
                  return "REQUEST_STK_GET_PROFILE";
              case RIL_REQUEST_STK_SET_PROFILE:
                  return "REQUEST_STK_SET_PROFILE";
              case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND:
                  return "REQUEST_STK_SEND_ENVELOPE_COMMAND";
              case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE:
                  return "REQUEST_STK_SEND_TERMINAL_RESPONSE";
              case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM:
                  return "REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM";
              case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: return "REQUEST_EXPLICIT_CALL_TRANSFER";
              case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE:
                  return "REQUEST_SET_PREFERRED_NETWORK_TYPE";
              case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE:
                  return "REQUEST_GET_PREFERRED_NETWORK_TYPE";
              case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS:
                  return "REQUEST_GET_NEIGHBORING_CELL_IDS";
              case RIL_REQUEST_SET_LOCATION_UPDATES:
                  return "REQUEST_SET_LOCATION_UPDATES";
              case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE:
                  return "RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE";
              case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE:
                  return "RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE";
              case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE:
                  return "RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE";
              case RIL_REQUEST_SET_TTY_MODE:
                  return "RIL_REQUEST_SET_TTY_MODE";
              case RIL_REQUEST_QUERY_TTY_MODE:
                  return "RIL_REQUEST_QUERY_TTY_MODE";
              case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE:
                  return "RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE";
              case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE:
                  return "RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE";
              case RIL_REQUEST_CDMA_FLASH:
                  return "RIL_REQUEST_CDMA_FLASH";
              case RIL_REQUEST_CDMA_BURST_DTMF:
                  return "RIL_REQUEST_CDMA_BURST_DTMF";
              case RIL_REQUEST_CDMA_SEND_SMS:
                  return "RIL_REQUEST_CDMA_SEND_SMS";
              case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE:
                  return "RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE";
              case RIL_REQUEST_GSM_GET_BROADCAST_CONFIG:
                  return "RIL_REQUEST_GSM_GET_BROADCAST_CONFIG";
              case RIL_REQUEST_GSM_SET_BROADCAST_CONFIG:
                  return "RIL_REQUEST_GSM_SET_BROADCAST_CONFIG";
              case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG:
                  return "RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG";
              case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG:
                  return "RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG";
              case RIL_REQUEST_GSM_BROADCAST_ACTIVATION:
                  return "RIL_REQUEST_GSM_BROADCAST_ACTIVATION";
              case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY:
                  return "RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY";
              case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION:
                  return "RIL_REQUEST_CDMA_BROADCAST_ACTIVATION";
              case RIL_REQUEST_CDMA_SUBSCRIPTION:
                  return "RIL_REQUEST_CDMA_SUBSCRIPTION";
              case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM:
                  return "RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM";
              case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM:
                  return "RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM";
              case RIL_REQUEST_DEVICE_IDENTITY:
                  return "RIL_REQUEST_DEVICE_IDENTITY";
              case RIL_REQUEST_GET_SMSC_ADDRESS:
                  return "RIL_REQUEST_GET_SMSC_ADDRESS";
              case RIL_REQUEST_SET_SMSC_ADDRESS:
                  return "RIL_REQUEST_SET_SMSC_ADDRESS";
              case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE:
                  return "REQUEST_EXIT_EMERGENCY_CALLBACK_MODE";
              case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS:
                  return "RIL_REQUEST_REPORT_SMS_MEMORY_STATUS";
              case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING:
                  return "RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING";
              case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE:
                  return "RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE";
              case RIL_REQUEST_ISIM_AUTHENTICATION:
                  return "RIL_REQUEST_ISIM_AUTHENTICATION";
              case RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU:
                  return "RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU";
              case RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS:
                  return "RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS";
              case RIL_REQUEST_VOICE_RADIO_TECH:
                  return "RIL_REQUEST_VOICE_RADIO_TECH";
              case RIL_REQUEST_GET_CELL_INFO_LIST:
                  return "RIL_REQUEST_GET_CELL_INFO_LIST";
              case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE:
                  return "RIL_REQUEST_SET_CELL_INFO_LIST_RATE";
              case RIL_REQUEST_SET_INITIAL_ATTACH_APN:
                  return "RIL_REQUEST_SET_INITIAL_ATTACH_APN";
              case RIL_REQUEST_SET_DATA_PROFILE:
                  return "RIL_REQUEST_SET_DATA_PROFILE";
              case RIL_REQUEST_IMS_REGISTRATION_STATE:
                  return "RIL_REQUEST_IMS_REGISTRATION_STATE";
              case RIL_REQUEST_IMS_SEND_SMS:
                  return "RIL_REQUEST_IMS_SEND_SMS";
              case RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC:
                  return "RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC";
              case RIL_REQUEST_SIM_OPEN_CHANNEL:
                  return "RIL_REQUEST_SIM_OPEN_CHANNEL";
              case RIL_REQUEST_SIM_CLOSE_CHANNEL:
                  return "RIL_REQUEST_SIM_CLOSE_CHANNEL";
              case RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL:
                  return "RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL";
              case RIL_REQUEST_NV_READ_ITEM:
                  return "RIL_REQUEST_NV_READ_ITEM";
              case RIL_REQUEST_NV_WRITE_ITEM:
                  return "RIL_REQUEST_NV_WRITE_ITEM";
              case RIL_REQUEST_NV_WRITE_CDMA_PRL:
                  return "RIL_REQUEST_NV_WRITE_CDMA_PRL";
              case RIL_REQUEST_NV_RESET_CONFIG:
                  return "RIL_REQUEST_NV_RESET_CONFIG";
              case RIL_REQUEST_SET_UICC_SUBSCRIPTION:
                  return "RIL_REQUEST_SET_UICC_SUBSCRIPTION";
              case RIL_REQUEST_ALLOW_DATA:
                  return "RIL_REQUEST_ALLOW_DATA";
              case RIL_REQUEST_GET_HARDWARE_CONFIG:
                  return "GET_HARDWARE_CONFIG";
              case RIL_REQUEST_SIM_AUTHENTICATION:
                  return "RIL_REQUEST_SIM_AUTHENTICATION";
              case RIL_REQUEST_SHUTDOWN:
                  return "RIL_REQUEST_SHUTDOWN";
              case RIL_REQUEST_SET_RADIO_CAPABILITY:
                  return "RIL_REQUEST_SET_RADIO_CAPABILITY";
              case RIL_REQUEST_GET_RADIO_CAPABILITY:
                  return "RIL_REQUEST_GET_RADIO_CAPABILITY";
              case RIL_REQUEST_START_LCE:
                  return "RIL_REQUEST_START_LCE";
              case RIL_REQUEST_STOP_LCE:
                  return "RIL_REQUEST_STOP_LCE";
              case RIL_REQUEST_PULL_LCEDATA:
                  return "RIL_REQUEST_PULL_LCEDATA";
              case RIL_REQUEST_GET_ACTIVITY_INFO:
                  return "RIL_REQUEST_GET_ACTIVITY_INFO";
              case RIL_REQUEST_SET_ALLOWED_CARRIERS:
                  return "RIL_REQUEST_SET_ALLOWED_CARRIERS";
              case RIL_REQUEST_GET_ALLOWED_CARRIERS:
                  return "RIL_REQUEST_GET_ALLOWED_CARRIERS";
              case RIL_REQUEST_SET_SIM_CARD_POWER:
                  return "RIL_REQUEST_SET_SIM_CARD_POWER";
              case RIL_REQUEST_SEND_DEVICE_STATE:
                  return "RIL_REQUEST_SEND_DEVICE_STATE";
              case RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER:
                  return "RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER";
              case RIL_RESPONSE_ACKNOWLEDGEMENT:
                  return "RIL_RESPONSE_ACKNOWLEDGEMENT";
              case RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION:
                  return "RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION";
              case RIL_REQUEST_START_NETWORK_SCAN:
                  return "RIL_REQUEST_START_NETWORK_SCAN";
              case RIL_REQUEST_STOP_NETWORK_SCAN:
                  return "RIL_REQUEST_STOP_NETWORK_SCAN";
              case RIL_REQUEST_GET_SLOT_STATUS:
                  return "RIL_REQUEST_GET_SLOT_STATUS";
              case RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING:
                  return "RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING";
              case RIL_REQUEST_START_KEEPALIVE:
                  return "RIL_REQUEST_START_KEEPALIVE";
              case RIL_REQUEST_STOP_KEEPALIVE:
                  return "RIL_REQUEST_STOP_KEEPALIVE";
              case RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA:
                  return "RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA";
              case RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA:
                  return "RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA";
              case RIL_REQUEST_ENABLE_MODEM:
                  return "RIL_REQUEST_ENABLE_MODEM";
              case RIL_REQUEST_GET_MODEM_STATUS:
                  return "RIL_REQUEST_GET_MODEM_STATUS";
              default: return "<unknown request>";
          }
      }
  
      @UnsupportedAppUsage
      static String responseToString(int request) {
          switch(request) {
              case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
                  return "UNSOL_RESPONSE_RADIO_STATE_CHANGED";
              case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
                  return "UNSOL_RESPONSE_CALL_STATE_CHANGED";
              case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED:
                  return "UNSOL_RESPONSE_NETWORK_STATE_CHANGED";
              case RIL_UNSOL_RESPONSE_NEW_SMS:
                  return "UNSOL_RESPONSE_NEW_SMS";
              case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT:
                  return "UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT";
              case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM:
                  return "UNSOL_RESPONSE_NEW_SMS_ON_SIM";
              case RIL_UNSOL_ON_USSD:
                  return "UNSOL_ON_USSD";
              case RIL_UNSOL_ON_USSD_REQUEST:
                  return "UNSOL_ON_USSD_REQUEST";
              case RIL_UNSOL_NITZ_TIME_RECEIVED:
                  return "UNSOL_NITZ_TIME_RECEIVED";
              case RIL_UNSOL_SIGNAL_STRENGTH:
                  return "UNSOL_SIGNAL_STRENGTH";
              case RIL_UNSOL_DATA_CALL_LIST_CHANGED:
                  return "UNSOL_DATA_CALL_LIST_CHANGED";
              case RIL_UNSOL_SUPP_SVC_NOTIFICATION:
                  return "UNSOL_SUPP_SVC_NOTIFICATION";
              case RIL_UNSOL_STK_SESSION_END:
                  return "UNSOL_STK_SESSION_END";
              case RIL_UNSOL_STK_PROACTIVE_COMMAND:
                  return "UNSOL_STK_PROACTIVE_COMMAND";
              case RIL_UNSOL_STK_EVENT_NOTIFY:
                  return "UNSOL_STK_EVENT_NOTIFY";
              case RIL_UNSOL_STK_CALL_SETUP:
                  return "UNSOL_STK_CALL_SETUP";
              case RIL_UNSOL_SIM_SMS_STORAGE_FULL:
                  return "UNSOL_SIM_SMS_STORAGE_FULL";
              case RIL_UNSOL_SIM_REFRESH:
                  return "UNSOL_SIM_REFRESH";
              case RIL_UNSOL_CALL_RING:
                  return "UNSOL_CALL_RING";
              case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED:
                  return "UNSOL_RESPONSE_SIM_STATUS_CHANGED";
              case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS:
                  return "UNSOL_RESPONSE_CDMA_NEW_SMS";
              case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS:
                  return "UNSOL_RESPONSE_NEW_BROADCAST_SMS";
              case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL:
                  return "UNSOL_CDMA_RUIM_SMS_STORAGE_FULL";
              case RIL_UNSOL_RESTRICTED_STATE_CHANGED:
                  return "UNSOL_RESTRICTED_STATE_CHANGED";
              case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE:
                  return "UNSOL_ENTER_EMERGENCY_CALLBACK_MODE";
              case RIL_UNSOL_CDMA_CALL_WAITING:
                  return "UNSOL_CDMA_CALL_WAITING";
              case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS:
                  return "UNSOL_CDMA_OTA_PROVISION_STATUS";
              case RIL_UNSOL_CDMA_INFO_REC:
                  return "UNSOL_CDMA_INFO_REC";
              case RIL_UNSOL_OEM_HOOK_RAW:
                  return "UNSOL_OEM_HOOK_RAW";
              case RIL_UNSOL_RINGBACK_TONE:
                  return "UNSOL_RINGBACK_TONE";
              case RIL_UNSOL_RESEND_INCALL_MUTE:
                  return "UNSOL_RESEND_INCALL_MUTE";
              case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
                  return "CDMA_SUBSCRIPTION_SOURCE_CHANGED";
              case RIL_UNSOl_CDMA_PRL_CHANGED:
                  return "UNSOL_CDMA_PRL_CHANGED";
              case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE:
                  return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE";
              case RIL_UNSOL_RIL_CONNECTED:
                  return "UNSOL_RIL_CONNECTED";
              case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED:
                  return "UNSOL_VOICE_RADIO_TECH_CHANGED";
              case RIL_UNSOL_CELL_INFO_LIST:
                  return "UNSOL_CELL_INFO_LIST";
              case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED:
                  return "UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED";
              case RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED:
                  return "RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED";
              case RIL_UNSOL_SRVCC_STATE_NOTIFY:
                  return "UNSOL_SRVCC_STATE_NOTIFY";
              case RIL_UNSOL_HARDWARE_CONFIG_CHANGED:
                  return "RIL_UNSOL_HARDWARE_CONFIG_CHANGED";
              case RIL_UNSOL_RADIO_CAPABILITY:
                  return "RIL_UNSOL_RADIO_CAPABILITY";
              case RIL_UNSOL_ON_SS:
                  return "UNSOL_ON_SS";
              case RIL_UNSOL_STK_CC_ALPHA_NOTIFY:
                  return "UNSOL_STK_CC_ALPHA_NOTIFY";
              case RIL_UNSOL_LCEDATA_RECV:
                  return "UNSOL_LCE_INFO_RECV";
              case RIL_UNSOL_PCO_DATA:
                  return "UNSOL_PCO_DATA";
              case RIL_UNSOL_MODEM_RESTART:
                  return "UNSOL_MODEM_RESTART";
              case RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION:
                  return "RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION";
              case RIL_UNSOL_NETWORK_SCAN_RESULT:
                  return "RIL_UNSOL_NETWORK_SCAN_RESULT";
              case RIL_UNSOL_ICC_SLOT_STATUS:
                  return "RIL_UNSOL_ICC_SLOT_STATUS";
              case RIL_UNSOL_KEEPALIVE_STATUS:
                  return "RIL_UNSOL_KEEPALIVE_STATUS";
              case RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG:
                  return "RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG";
              default:
                  return "<unknown response>";
          }
      }
  
      @UnsupportedAppUsage
      void riljLog(String msg) {
          Rlog.d(RILJ_LOG_TAG, msg + (" [SUB" + mPhoneId + "]"));
      }
  
      void riljLoge(String msg) {
          Rlog.e(RILJ_LOG_TAG, msg + (" [SUB" + mPhoneId + "]"));
      }
  
      void riljLoge(String msg, Exception e) {
          Rlog.e(RILJ_LOG_TAG, msg + (" [SUB" + mPhoneId + "]"), e);
      }
  
      void riljLogv(String msg) {
          Rlog.v(RILJ_LOG_TAG, msg + (" [SUB" + mPhoneId + "]"));
      }
  
      @UnsupportedAppUsage
      void unsljLog(int response) {
          riljLog("[UNSL]< " + responseToString(response));
      }
  
      @UnsupportedAppUsage
      void unsljLogMore(int response, String more) {
          riljLog("[UNSL]< " + responseToString(response) + " " + more);
      }
  
      @UnsupportedAppUsage
      void unsljLogRet(int response, Object ret) {
          riljLog("[UNSL]< " + responseToString(response) + " " + retToString(response, ret));
      }
  
      @UnsupportedAppUsage
      void unsljLogvRet(int response, Object ret) {
          riljLogv("[UNSL]< " + responseToString(response) + " " + retToString(response, ret));
      }
  
      @Override
      public void setPhoneType(int phoneType) { // Called by GsmCdmaPhone
          if (RILJ_LOGD) riljLog("setPhoneType=" + phoneType + " old value=" + mPhoneType);
          mPhoneType = phoneType;
      }
  
      /* (non-Javadoc)
       * @see com.android.internal.telephony.BaseCommands#testingEmergencyCall()
       */
      @Override
      public void testingEmergencyCall() {
          if (RILJ_LOGD) riljLog("testingEmergencyCall");
          mTestingEmergencyCall.set(true);
      }
  
      public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
          pw.println("RIL: " + this);
          pw.println(" mWakeLock=" + mWakeLock);
          pw.println(" mWakeLockTimeout=" + mWakeLockTimeout);
          synchronized (mRequestList) {
              synchronized (mWakeLock) {
                  pw.println(" mWakeLockCount=" + mWakeLockCount);
              }
              int count = mRequestList.size();
              pw.println(" mRequestList count=" + count);
              for (int i = 0; i < count; i++) {
                  RILRequest rr = mRequestList.valueAt(i);
                  pw.println("  [" + rr.mSerial + "] " + requestToString(rr.mRequest));
              }
          }
          pw.println(" mLastNITZTimeInfo=" + Arrays.toString(mLastNITZTimeInfo));
          pw.println(" mTestingEmergencyCall=" + mTestingEmergencyCall.get());
          mClientWakelockTracker.dumpClientRequestTracker(pw);
      }
  
      public List<ClientRequestStats> getClientRequestStats() {
          return mClientWakelockTracker.getClientRequestStats();
      }
  
      /** Append the data to the end of an ArrayList */
      public static void appendPrimitiveArrayToArrayList(byte[] src, ArrayList<Byte> dst) {
          for (byte b : src) {
              dst.add(b);
          }
      }
  
      public static ArrayList<Byte> primitiveArrayToArrayList(byte[] arr) {
          ArrayList<Byte> arrayList = new ArrayList<>(arr.length);
          for (byte b : arr) {
              arrayList.add(b);
          }
          return arrayList;
      }
  
      /** Convert a primitive int array to an ArrayList<Integer>. */
      public static ArrayList<Integer> primitiveArrayToArrayList(int[] arr) {
          ArrayList<Integer> arrayList = new ArrayList<>(arr.length);
          for (int i : arr) {
              arrayList.add(i);
          }
          return arrayList;
      }
  
      /** Convert an ArrayList of Bytes to an exactly-sized primitive array */
      public static byte[] arrayListToPrimitiveArray(ArrayList<Byte> bytes) {
          byte[] ret = new byte[bytes.size()];
          for (int i = 0; i < ret.length; i++) {
              ret[i] = bytes.get(i);
          }
          return ret;
      }
  
      static ArrayList<HardwareConfig> convertHalHwConfigList(
              ArrayList<android.hardware.radio.V1_0.HardwareConfig> hwListRil,
              RIL ril) {
          int num;
          ArrayList<HardwareConfig> response;
          HardwareConfig hw;
  
          num = hwListRil.size();
          response = new ArrayList<HardwareConfig>(num);
  
          if (RILJ_LOGV) {
              ril.riljLog("convertHalHwConfigList: num=" + num);
          }
          for (android.hardware.radio.V1_0.HardwareConfig hwRil : hwListRil) {
              int type = hwRil.type;
              switch(type) {
                  case HardwareConfig.DEV_HARDWARE_TYPE_MODEM: {
                      hw = new HardwareConfig(type);
                      HardwareConfigModem hwModem = hwRil.modem.get(0);
                      hw.assignModem(hwRil.uuid, hwRil.state, hwModem.rilModel, hwModem.rat,
                              hwModem.maxVoice, hwModem.maxData, hwModem.maxStandby);
                      break;
                  }
                  case HardwareConfig.DEV_HARDWARE_TYPE_SIM: {
                      hw = new HardwareConfig(type);
                      hw.assignSim(hwRil.uuid, hwRil.state, hwRil.sim.get(0).modemUuid);
                      break;
                  }
                  default: {
                      throw new RuntimeException(
                              "RIL_REQUEST_GET_HARDWARE_CONFIG invalid hardward type:" + type);
                  }
              }
  
              response.add(hw);
          }
  
          return response;
      }
  
      static RadioCapability convertHalRadioCapability(
              android.hardware.radio.V1_0.RadioCapability rcRil, RIL ril) {
          int session = rcRil.session;
          int phase = rcRil.phase;
          // convert to public bitmask {@link TelephonyManager.NetworkTypeBitMask}
          int rat = convertToNetworkTypeBitMask(rcRil.raf);
          String logicModemUuid = rcRil.logicalModemUuid;
          int status = rcRil.status;
  
          ril.riljLog("convertHalRadioCapability: session=" + session +
                  ", phase=" + phase +
                  ", rat=" + rat +
                  ", logicModemUuid=" + logicModemUuid +
                  ", status=" + status + ", rcRil.raf=" + rcRil.raf);
          RadioCapability rc = new RadioCapability(
                  ril.mPhoneId, session, phase, rat, logicModemUuid, status);
          return rc;
      }
  
      static LinkCapacityEstimate convertHalLceData(LceDataInfo halData, RIL ril) {
          final LinkCapacityEstimate lce = new LinkCapacityEstimate(
                  halData.lastHopCapacityKbps,
                  Byte.toUnsignedInt(halData.confidenceLevel),
                  halData.lceSuspended ? LinkCapacityEstimate.STATUS_SUSPENDED
                          : LinkCapacityEstimate.STATUS_ACTIVE);
  
          ril.riljLog("LCE capacity information received:" + lce);
          return lce;
      }
  
      static LinkCapacityEstimate convertHalLceData(
              android.hardware.radio.V1_2.LinkCapacityEstimate halData, RIL ril) {
          final LinkCapacityEstimate lce = new LinkCapacityEstimate(
                  halData.downlinkCapacityKbps,
                  halData.uplinkCapacityKbps);
          ril.riljLog("LCE capacity information received:" + lce);
          return lce;
      }
  
      /**
       * Convert CellInfo defined in 1.0/types.hal to CellInfo type.
       * @param records List of CellInfo defined in 1.0/types.hal
       * @return List of converted CellInfo object
       */
      @VisibleForTesting
      public static ArrayList<CellInfo> convertHalCellInfoList(
              ArrayList<android.hardware.radio.V1_0.CellInfo> records) {
          ArrayList<CellInfo> response = new ArrayList<CellInfo>(records.size());
  
          final long nanotime = SystemClock.elapsedRealtimeNanos();
          for (android.hardware.radio.V1_0.CellInfo record : records) {
              record.timeStamp = nanotime;
              response.add(CellInfo.create(record));
          }
  
          return response;
      }
  
      /**
       * Convert CellInfo defined in 1.2/types.hal to CellInfo type.
       * @param records List of CellInfo defined in 1.2/types.hal
       * @return List of converted CellInfo object
       */
      @VisibleForTesting
      public static ArrayList<CellInfo> convertHalCellInfoList_1_2(
              ArrayList<android.hardware.radio.V1_2.CellInfo> records) {
          ArrayList<CellInfo> response = new ArrayList<CellInfo>(records.size());
  
          final long nanotime = SystemClock.elapsedRealtimeNanos();
          for (android.hardware.radio.V1_2.CellInfo record : records) {
              record.timeStamp = nanotime;
              response.add(CellInfo.create(record));
          }
          return response;
      }
  
      /**
       * Fixup for SignalStrength 1.0 to Assume GSM to WCDMA when
       * The current RAT type is one of the UMTS RATs.
       * @param signalStrength the initial signal strength
       * @return a new SignalStrength if RAT is UMTS or existing SignalStrength
       */
      public SignalStrength fixupSignalStrength10(SignalStrength signalStrength) {
          List<CellSignalStrengthGsm> gsmList = signalStrength.getCellSignalStrengths(
                  CellSignalStrengthGsm.class);
          // If GSM is not the primary type, then bail out; no fixup needed.
          if (gsmList.isEmpty() || !gsmList.get(0).isValid()) {
              return signalStrength;
          }
  
          CellSignalStrengthGsm gsmStrength = gsmList.get(0);
  
          // Use the voice RAT which is a guarantee in GSM and UMTS
          int voiceRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
          Phone phone = PhoneFactory.getPhone(mPhoneId);
          if (phone != null) {
              ServiceState ss = phone.getServiceState();
              if (ss != null) {
                  voiceRat = ss.getRilVoiceRadioTechnology();
              }
          }
          switch (voiceRat) {
              case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS: /* fallthrough */
              case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA: /* fallthrough */
              case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA: /* fallthrough */
              case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA: /* fallthrough */
              case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP: /* fallthrough */
                  break;
              default:
                  // If we are not currently on WCDMA/HSPA, then we don't need to do a fixup.
                  return signalStrength;
          }
  
          // The service state reports WCDMA, and the SignalStrength is reported for GSM, so at this
          // point we take an educated guess that the GSM SignalStrength report is actually for
          // WCDMA. Also, if we are in WCDMA/GSM we can safely assume that there are no other valid
          // signal strength reports (no SRLTE, which is the only supported case in HAL 1.0).
          // Thus, we just construct a new SignalStrength and migrate RSSI and BER from the
          // GSM report to the WCDMA report, leaving everything else empty.
          return new SignalStrength(
                  new CellSignalStrengthCdma(), new CellSignalStrengthGsm(),
                  new CellSignalStrengthWcdma(gsmStrength.getRssi(),
                          gsmStrength.getBitErrorRate(),
                          CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE),
                  new CellSignalStrengthTdscdma(), new CellSignalStrengthLte(),
                  new CellSignalStrengthNr());
      }
  
      /**
       * Convert CellInfo defined in 1.4/types.hal to CellInfo type.
       * @param records List of CellInfo defined in 1.4/types.hal.
       * @return List of converted CellInfo object.
       */
      @VisibleForTesting
      public static ArrayList<CellInfo> convertHalCellInfoList_1_4(
              ArrayList<android.hardware.radio.V1_4.CellInfo> records) {
          ArrayList<CellInfo> response = new ArrayList<CellInfo>(records.size());
  
          final long nanotime = SystemClock.elapsedRealtimeNanos();
          for (android.hardware.radio.V1_4.CellInfo record : records) {
              response.add(CellInfo.create(record, nanotime));
          }
          return response;
      }
  
      /**
       * Convert SetupDataCallResult defined in 1.0 or 1.4/types.hal into DataCallResponse
       * @param dcResult setup data call result
       * @return converted DataCallResponse object
       */
      @VisibleForTesting
      public static DataCallResponse convertDataCallResult(Object dcResult) {
          if (dcResult == null) return null;
  
          int cause, suggestedRetryTime, cid, active, mtu;
          String ifname;
          int protocolType;
          String[] addresses = null;
          String[] dnses = null;
          String[] gateways = null;
          String[] pcscfs = null;
          if (dcResult instanceof android.hardware.radio.V1_0.SetupDataCallResult) {
              final android.hardware.radio.V1_0.SetupDataCallResult result =
                      (android.hardware.radio.V1_0.SetupDataCallResult) dcResult;
              cause = result.status;
              suggestedRetryTime = result.suggestedRetryTime;
              cid = result.cid;
              active = result.active;
              protocolType = ApnSetting.getProtocolIntFromString(result.type);
              ifname = result.ifname;
              if (!TextUtils.isEmpty(result.addresses)) {
                  addresses = result.addresses.split("\\s+");
              }
              if (!TextUtils.isEmpty(result.dnses)) {
                  dnses = result.dnses.split("\\s+");
              }
              if (!TextUtils.isEmpty(result.gateways)) {
                  gateways = result.gateways.split("\\s+");
              }
              if (!TextUtils.isEmpty(result.pcscf)) {
                  pcscfs = result.pcscf.split("\\s+");
              }
              mtu = result.mtu;
          } else if (dcResult instanceof android.hardware.radio.V1_4.SetupDataCallResult) {
              final android.hardware.radio.V1_4.SetupDataCallResult result =
                      (android.hardware.radio.V1_4.SetupDataCallResult) dcResult;
              cause = result.cause;
              suggestedRetryTime = result.suggestedRetryTime;
              cid = result.cid;
              active = result.active;
              protocolType = result.type;
              ifname = result.ifname;
              addresses = result.addresses.stream().toArray(String[]::new);
              dnses = result.dnses.stream().toArray(String[]::new);
              gateways = result.gateways.stream().toArray(String[]::new);
              pcscfs = result.pcscf.stream().toArray(String[]::new);
              mtu = result.mtu;
          } else {
              Rlog.e(RILJ_LOG_TAG, "Unsupported SetupDataCallResult " + dcResult);
              return null;
          }
  
          // Process address
          List<LinkAddress> laList = new ArrayList<>();
          if (addresses != null) {
              for (String address : addresses) {
                  address = address.trim();
                  if (address.isEmpty()) continue;
  
                  try {
                      LinkAddress la;
                      // Check if the address contains prefix length. If yes, LinkAddress
                      // can parse that.
                      if (address.split("/").length == 2) {
                          la = new LinkAddress(address);
                      } else {
                          InetAddress ia = NetworkUtils.numericToInetAddress(address);
                          la = new LinkAddress(ia, (ia instanceof Inet4Address) ? 32 : 128);
                      }
  
                      laList.add(la);
                  } catch (IllegalArgumentException e) {
                      Rlog.e(RILJ_LOG_TAG, "Unknown address: " + address, e);
                  }
              }
          }
  
          // Process dns
          List<InetAddress> dnsList = new ArrayList<>();
          if (dnses != null) {
              for (String dns : dnses) {
                  dns = dns.trim();
                  InetAddress ia;
                  try {
                      ia = NetworkUtils.numericToInetAddress(dns);
                      dnsList.add(ia);
                  } catch (IllegalArgumentException e) {
                      Rlog.e(RILJ_LOG_TAG, "Unknown dns: " + dns, e);
                  }
              }
          }
  
          // Process gateway
          List<InetAddress> gatewayList = new ArrayList<>();
          if (gateways != null) {
              for (String gateway : gateways) {
                  gateway = gateway.trim();
                  InetAddress ia;
                  try {
                      ia = NetworkUtils.numericToInetAddress(gateway);
                      gatewayList.add(ia);
                  } catch (IllegalArgumentException e) {
                      Rlog.e(RILJ_LOG_TAG, "Unknown gateway: " + gateway, e);
                  }
              }
          }
  
          // Process gateway
          List<InetAddress> pcscfList = new ArrayList<>();
          if (pcscfs != null) {
              for (String pcscf : pcscfs) {
                  pcscf = pcscf.trim();
                  InetAddress ia;
                  try {
                      ia = NetworkUtils.numericToInetAddress(pcscf);
                      pcscfList.add(ia);
                  } catch (IllegalArgumentException e) {
                      Rlog.e(RILJ_LOG_TAG, "Unknown pcscf: " + pcscf, e);
                  }
              }
          }
  
          return new DataCallResponse(cause, suggestedRetryTime, cid, active, protocolType, ifname,
                  laList, dnsList, gatewayList, pcscfList, mtu);
      }
  
      /**
       * Convert SetupDataCallResult defined in 1.0 or 1.4/types.hal into DataCallResponse
       * @param dataCallResultList List of SetupDataCallResult defined in 1.0 or 1.4/types.hal
       * @return List of converted DataCallResponse object
       */
      @VisibleForTesting
      public static ArrayList<DataCallResponse> convertDataCallResultList(
              List<? extends Object> dataCallResultList) {
          ArrayList<DataCallResponse> response =
                  new ArrayList<DataCallResponse>(dataCallResultList.size());
  
          for (Object obj : dataCallResultList) {
              response.add(convertDataCallResult(obj));
          }
          return response;
      }
  
      /**
       * Get the HAL version.
       *
       * @return the current HalVersion
       */
      public HalVersion getHalVersion() {
          return mRadioVersion;
      }
  }
  