Add RadioImaProxy, ImsResponse, and ImsIndication for IRadioIms

Bug: 216387835
Test: build & flash
Change-Id: I278089c761cc9c5066db084d0169c1607ebe1b62
Merged-In: I278089c761cc9c5066db084d0169c1607ebe1b62
diff --git a/Android.bp b/Android.bp
index d784fc8..eb16ba5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -85,6 +85,7 @@
         "android.hardware.radio-V1.6-java",
         "android.hardware.radio.config-V1-java",
         "android.hardware.radio.data-V1-java",
+        "android.hardware.radio.ims-V1-java",
         "android.hardware.radio.messaging-V1-java",
         "android.hardware.radio.modem-V1-java",
         "android.hardware.radio.network-V2-java",
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 764458a..25cd6c7 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -115,6 +115,8 @@
     protected RegistrantList mSimPhonebookChangedRegistrants = new RegistrantList();
     protected RegistrantList mSimPhonebookRecordsReceivedRegistrants = new RegistrantList();
     protected RegistrantList mEmergencyNetworkScanRegistrants = new RegistrantList();
+    protected RegistrantList mConnectionSetupFailureRegistrants = new RegistrantList();
+    protected RegistrantList mAccessAllowedRegistrants = new RegistrantList();
 
     @UnsupportedAppUsage
     protected Registrant mGsmSmsRegistrant;
@@ -1155,4 +1157,24 @@
     public void unregisterForEmergencyNetworkScan(Handler h) {
         mEmergencyNetworkScanRegistrants.remove(h);
     }
+
+    @Override
+    public void registerForConnectionSetupFailure(Handler h, int what, Object obj) {
+        mConnectionSetupFailureRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForConnectionSetupFailure(Handler h) {
+        mConnectionSetupFailureRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForAccessAllowed(Handler h, int what, Object obj) {
+        mAccessAllowedRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForAccessAllowed(Handler h) {
+        mAccessAllowedRegistrants.remove(h);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index cdde40e..329abee 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -2747,6 +2747,38 @@
      public void unregisterForSimPhonebookRecordsReceived(Handler h);
 
     /**
+     * Registers for notifications when connection setup fails.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForConnectionSetupFailure(Handler h, int what, Object obj) {}
+
+    /**
+     * Unregisters for notifications when connection setup fails.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForConnectionSetupFailure(Handler h) {}
+
+    /**
+     * Register for notifications when IMS traffic access is allowed
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForAccessAllowed(Handler h, int what, Object obj) {}
+
+    /**
+     * Unregister for notifications when IMS traffic access is allowed
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForAccessAllowed(Handler h) {}
+
+    /**
      * Set the UE's usage setting.
      *
      * @param result Callback message containing the success or failure status.
@@ -2777,4 +2809,39 @@
      * @param h Handler to be removed from the registrant list.
      */
     default void unregisterForEmergencyNetworkScan(Handler h) {}
+
+    /**
+     * Provides a list of SRVCC call information to radio
+     *
+     * @param srvccConnections the list of connections.
+     */
+    default void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {}
+
+    /**
+     * Updates the IMS registration information to the radio.
+     *
+     * @param state the current IMS registration state.
+     * @param ipcan the type of IP connectivity access network where IMS features are registered.
+     * @param reason a failure reason for IMS registration.
+     * @param features IMS features such as VOICE, VIDEO and SMS.
+     */
+    default void updateImsRegistrationInfo(int state,
+            int ipcan, int reason, int features, Message result) {}
+
+    /**
+     * Notifies the NAS and RRC layers of the radio the type of upcoming IMS traffic.
+     *
+     * @param token The token of the request.
+     * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc.
+     * @param isStart true when the traffic flow starts, false when traffic flow stops.
+     */
+    default void notifyImsTraffic(int token, int trafficType, boolean isStart, Message result) {}
+
+    /**
+     * Checks access class barring checks based on ImsTrafficType.
+     *
+     * @param token The token of the request.
+     * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc.
+     */
+    default void performAcbCheck(int token, int trafficType, Message result) {}
 }
diff --git a/src/java/com/android/internal/telephony/ImsIndication.java b/src/java/com/android/internal/telephony/ImsIndication.java
new file mode 100644
index 0000000..5436607
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ImsIndication.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.RIL_UNSOL_ACCESS_ALLOWED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CONNECTION_SETUP_FAILURE;
+
+import android.hardware.radio.ims.IRadioImsIndication;
+import android.os.AsyncResult;
+
+/**
+ * Interface declaring unsolicited radio indications for IMS APIs.
+ */
+public class ImsIndication extends IRadioImsIndication.Stub {
+    private final RIL mRil;
+
+    public ImsIndication(RIL ril) {
+        mRil = ril;
+    }
+
+    /**
+     * Fired by radio when any IMS traffic is not sent to network due to any failure
+     * on cellular networks.
+     *
+     * @param indicationType Type of radio indication.
+     * @param token The token provided by {@link #notifyImsTraffic} or {@link #performACBcheck}.
+     * @param info Connection failure information.
+     */
+    public void onConnectionSetupFailure(int indicationType, int token,
+            android.hardware.radio.ims.ConnectionFailureInfo info) {
+        mRil.processIndication(RIL.IMS_SERVICE, indicationType);
+
+        int[] response = new int[4];
+        response[0] = token;
+        response[1] = info.failureReason;
+        response[2] = info.causeCode;
+        response[3] = info.waitTimeMillis;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CONNECTION_SETUP_FAILURE, response);
+
+        mRil.mConnectionSetupFailureRegistrants.notifyRegistrants(
+                new AsyncResult(null, response, null));
+    }
+
+    /**
+     * Fired by radio in response to {@link #performAcbCheck}
+     * if the access class check is allowed for the requested traffic type.
+     *
+     * @param indicationType Type of radio indication
+     * @param token The token provided by {@link #performAcbCheck}
+     */
+    public void onAccessAllowed(int indicationType, int token) {
+        mRil.processIndication(RIL.IMS_SERVICE, indicationType);
+
+        int[] response = new int[1];
+        response[0] = token;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_ACCESS_ALLOWED, response);
+
+        mRil.mAccessAllowedRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ImsResponse.java b/src/java/com/android/internal/telephony/ImsResponse.java
new file mode 100644
index 0000000..4664c1e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ImsResponse.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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 android.hardware.radio.RadioResponseInfo;
+import android.hardware.radio.ims.IRadioImsResponse;
+
+/**
+ * Interface declaring response functions to solicited radio requests for IMS APIs.
+ */
+public class ImsResponse extends IRadioImsResponse.Stub {
+    private final RIL mRil;
+
+    public ImsResponse(RIL ril) {
+        mRil = ril;
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void setSrvccCallInfoResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(RIL.IMS_SERVICE, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void updateImsRegistrationInfoResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(RIL.IMS_SERVICE, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void notifyImsTrafficResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(RIL.IMS_SERVICE, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void performAcbCheckResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(RIL.IMS_SERVICE, mRil, info);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index c9cfc4d..11ba496 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -208,8 +208,9 @@
     static final int NETWORK_SERVICE = 4;
     static final int SIM_SERVICE = 5;
     static final int VOICE_SERVICE = 6;
+    static final int IMS_SERVICE = 7;
     static final int MIN_SERVICE_IDX = RADIO_SERVICE;
-    static final int MAX_SERVICE_IDX = VOICE_SERVICE;
+    static final int MAX_SERVICE_IDX = IMS_SERVICE;
 
     /**
      * An array of sets that records if services are disabled in the HAL for a specific phone ID
@@ -236,6 +237,8 @@
     private volatile IRadio mRadioProxy = null;
     private DataResponse mDataResponse;
     private DataIndication mDataIndication;
+    private ImsResponse mImsResponse;
+    private ImsIndication mImsIndication;
     private MessagingResponse mMessagingResponse;
     private MessagingIndication mMessagingIndication;
     private ModemResponse mModemResponse;
@@ -707,8 +710,8 @@
 
     /**
      * Returns a {@link RadioDataProxy}, {@link RadioMessagingProxy}, {@link RadioModemProxy},
-     * {@link RadioNetworkProxy}, {@link RadioSimProxy}, {@link RadioVoiceProxy}, or an empty {@link RadioServiceProxy}
-     * if the service is not available.
+     * {@link RadioNetworkProxy}, {@link RadioSimProxy}, {@link RadioVoiceProxy},
+     * {@link RadioImsProxy}, or null if the service is not available.
      */
     @NonNull
     public <T extends RadioServiceProxy> T getRadioServiceProxy(Class<T> serviceClass,
@@ -731,6 +734,9 @@
         if (serviceClass == RadioVoiceProxy.class) {
             return (T) getRadioServiceProxy(VOICE_SERVICE, result);
         }
+        if (serviceClass == RadioImsProxy.class) {
+            return (T) getRadioServiceProxy(IMS_SERVICE, result);
+        }
         riljLoge("getRadioServiceProxy: unrecognized " + serviceClass);
         return null;
     }
@@ -851,6 +857,21 @@
                                             .asInterface(binder));
                         }
                         break;
+                    case IMS_SERVICE:
+                        if (mMockModem == null) {
+                            binder = ServiceManager.waitForDeclaredService(
+                                    android.hardware.radio.ims.IRadioIms.DESCRIPTOR + "/"
+                                            + HIDL_SERVICE_NAME[mPhoneId]);
+                        } else {
+                            binder = mMockModem.getServiceBinder(IMS_SERVICE);
+                        }
+                        if (binder != null) {
+                            mRadioVersion = RADIO_HAL_VERSION_2_1;
+                            ((RadioImsProxy) serviceProxy).setAidl(mRadioVersion,
+                                    android.hardware.radio.ims.IRadioIms.Stub
+                                            .asInterface(binder));
+                        }
+                        break;
                 }
 
                 if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
@@ -962,6 +983,12 @@
                                 ((RadioVoiceProxy) serviceProxy).getAidl().setResponseFunctions(
                                         mVoiceResponse, mVoiceIndication);
                                 break;
+                            case IMS_SERVICE:
+                                mDeathRecipients.get(service).linkToDeath(
+                                        ((RadioImsProxy) serviceProxy).getAidl().asBinder());
+                                ((RadioImsProxy) serviceProxy).getAidl().setResponseFunctions(
+                                        mImsResponse, mImsIndication);
+                                break;
                         }
                     } else {
                         if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
@@ -1062,6 +1089,8 @@
         mRadioIndication = new RadioIndication(this);
         mDataResponse = new DataResponse(this);
         mDataIndication = new DataIndication(this);
+        mImsResponse = new ImsResponse(this);
+        mImsIndication = new ImsIndication(this);
         mMessagingResponse = new MessagingResponse(this);
         mMessagingIndication = new MessagingIndication(this);
         mModemResponse = new ModemResponse(this);
@@ -1088,6 +1117,7 @@
             mServiceProxies.put(NETWORK_SERVICE, new RadioNetworkProxy());
             mServiceProxies.put(SIM_SERVICE, new RadioSimProxy());
             mServiceProxies.put(VOICE_SERVICE, new RadioVoiceProxy());
+            mServiceProxies.put(IMS_SERVICE, new RadioImsProxy());
         } else {
             mServiceProxies = proxies;
         }
@@ -5089,6 +5119,133 @@
         }
     }
 
+    @Override
+    public void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+        if (imsProxy.isEmpty()) return;
+        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SRVCC_CALL_INFO, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                // Do not log function arg for privacy
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            try {
+                imsProxy.setSrvccCallInfo(rr.mSerial,
+                        RILUtils.convertToHalSrvccCall(srvccConnections));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(IMS_SERVICE, "setSrvccCallInfo", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "setSrvccCallInfo: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    @Override
+    public void updateImsRegistrationInfo(int state,
+            int ipcan, int reason, int features, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+        if (imsProxy.isEmpty()) return;
+        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                // Do not log function arg for privacy
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            android.hardware.radio.ims.ImsRegistration registrationInfo =
+                    new android.hardware.radio.ims.ImsRegistration();
+            registrationInfo.state = state;
+            registrationInfo.ipcan = ipcan;
+            registrationInfo.reason = reason;
+            registrationInfo.features = features;
+
+            try {
+                imsProxy.updateImsRegistrationInfo(rr.mSerial, registrationInfo);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(IMS_SERVICE, "updateImsRegistrationInfo", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "updateImsRegistrationInfo: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    @Override
+    public void notifyImsTraffic(int token, int trafficType, boolean isStart, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+        if (imsProxy.isEmpty()) return;
+        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_NOTIFY_IMS_TRAFFIC, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            try {
+                imsProxy.notifyImsTraffic(rr.mSerial, token, trafficType, isStart);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(IMS_SERVICE, "notifyImsTraffic", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "notifyImsTraffic: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    @Override
+    public void performAcbCheck(int token, int trafficType, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+        if (imsProxy.isEmpty()) return;
+        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_PERFORM_ACB_CHECK, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            try {
+                imsProxy.performAcbCheck(rr.mSerial, token, trafficType);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(IMS_SERVICE, "performAcbCheck", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "performAcbCheck: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
     //***** Private Methods
     /**
      * This is a helper function to be called when an indication callback is called for any radio
@@ -5957,6 +6114,8 @@
                 return "SIM";
             case VOICE_SERVICE:
                 return "VOICE";
+            case IMS_SERVICE:
+                return "IMS";
             default:
                 return "UNKNOWN:" + service;
         }
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index 2d1107c..dc13cd4 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -116,6 +116,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_VONR_ENABLED;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_LAST_CALL_FAIL_CAUSE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_NOTIFY_IMS_TRAFFIC;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_NV_READ_ITEM;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_NV_RESET_CONFIG;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_NV_WRITE_CDMA_PRL;
@@ -123,6 +124,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_OEM_HOOK_RAW;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_OEM_HOOK_STRINGS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_OPERATOR;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_PERFORM_ACB_CHECK;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_PULL_LCEDATA;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_QUERY_AVAILABLE_NETWORKS;
@@ -168,6 +170,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIM_CARD_POWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SMSC_ADDRESS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SRVCC_CALL_INFO;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_TTY_MODE;
@@ -200,10 +203,12 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UDUB;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_RADIO_TECH;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_REGISTRATION_STATE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_WRITE_SMS_TO_SIM;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ACCESS_ALLOWED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_BARRING_INFO_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CALL_RING;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION;
@@ -214,6 +219,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CELL_INFO_LIST;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CONNECTION_SETUP_FAILURE;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DATA_CALL_LIST_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DC_RT_INFO_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NUMBER_LIST;
@@ -5042,6 +5048,14 @@
                 return "SET_USAGE_SETTING";
             case RIL_REQUEST_GET_USAGE_SETTING:
                 return "GET_USAGE_SETTING";
+            case RIL_REQUEST_SET_SRVCC_CALL_INFO:
+                return "SET_SRVCC_CALL_INFO";
+            case RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO:
+                return "UPDATE_IMS_REGISTRATION_INFO";
+            case RIL_REQUEST_NOTIFY_IMS_TRAFFIC:
+                return "NOTIFY_IMS_TRAFFIC";
+            case RIL_REQUEST_PERFORM_ACB_CHECK:
+                return "PERFORM_ACB_CHECK";
             default:
                 return "<unknown request " + request + ">";
         }
@@ -5174,6 +5188,10 @@
                 return "UNSOL_REGISTRATION_FAILED";
             case RIL_UNSOL_BARRING_INFO_CHANGED:
                 return "UNSOL_BARRING_INFO_CHANGED";
+            case RIL_UNSOL_ACCESS_ALLOWED:
+                return "UNSOL_ACCESS_ALLOWED";
+            case RIL_UNSOL_CONNECTION_SETUP_FAILURE:
+                return "UNSOL_CONNECTION_SETUP_FAILURE";
             default:
                 return "<unknown response>";
         }
@@ -5322,6 +5340,82 @@
         return sb.toString();
     }
 
+    /**
+     * Converts the list of call information for Single Radio Voice Call Continuity(SRVCC).
+     *
+     * @param srvccConnections The list of call information for SRVCC.
+     * @return The converted list of call information.
+     */
+    public static android.hardware.radio.ims.SrvccCall[] convertToHalSrvccCall(
+            SrvccConnection[] srvccConnections) {
+        if (srvccConnections == null) {
+            return new android.hardware.radio.ims.SrvccCall[0];
+        }
+
+        int length = srvccConnections.length;
+        android.hardware.radio.ims.SrvccCall[] srvccCalls =
+                new android.hardware.radio.ims.SrvccCall[length];
+
+        for (int i = 0; i < length; i++) {
+            srvccCalls[i] = new android.hardware.radio.ims.SrvccCall();
+            srvccCalls[i].index = i + 1;
+            srvccCalls[i].callType = srvccConnections[i].getType();
+            srvccCalls[i].callState = convertCallState(srvccConnections[i].getState());
+            srvccCalls[i].callSubstate = srvccConnections[i].getSubState();
+            srvccCalls[i].ringbackToneType = srvccConnections[i].getRingbackToneType();
+            srvccCalls[i].isMpty = srvccConnections[i].isMultiParty();
+            srvccCalls[i].isMT = srvccConnections[i].isIncoming();
+            srvccCalls[i].number = TextUtils.emptyIfNull(srvccConnections[i].getNumber());
+            srvccCalls[i].numPresentation =
+                    convertPresentation(srvccConnections[i].getNumberPresentation());
+            srvccCalls[i].name = TextUtils.emptyIfNull(srvccConnections[i].getName());
+            srvccCalls[i].namePresentation =
+                    convertPresentation(srvccConnections[i].getNamePresentation());
+        }
+
+        return srvccCalls;
+    }
+
+    /**
+     * Converts the call state.
+     *
+     * @param state The call state.
+     * @return The converted call state.
+     */
+    public static int convertCallState(Call.State state) {
+        switch (state) {
+            case ACTIVE: return android.hardware.radio.voice.Call.STATE_ACTIVE;
+            case HOLDING: return android.hardware.radio.voice.Call.STATE_HOLDING;
+            case DIALING: return android.hardware.radio.voice.Call.STATE_DIALING;
+            case ALERTING: return android.hardware.radio.voice.Call.STATE_ALERTING;
+            case INCOMING: return android.hardware.radio.voice.Call.STATE_INCOMING;
+            case WAITING: return android.hardware.radio.voice.Call.STATE_WAITING;
+            default:
+                throw new RuntimeException("illegal state " + state);
+        }
+    }
+
+    /**
+     * Converts the number presentation type for caller id display.
+     *
+     * @param presentation The number presentation type.
+     * @return The converted presentation type.
+     */
+    public static int convertPresentation(int presentation) {
+        switch (presentation) {
+            case PhoneConstants.PRESENTATION_ALLOWED:
+                return android.hardware.radio.voice.Call.PRESENTATION_ALLOWED;
+            case PhoneConstants.PRESENTATION_RESTRICTED:
+                return android.hardware.radio.voice.Call.PRESENTATION_RESTRICTED;
+            case PhoneConstants.PRESENTATION_UNKNOWN:
+                return android.hardware.radio.voice.Call.PRESENTATION_UNKNOWN;
+            case PhoneConstants.PRESENTATION_PAYPHONE:
+                return android.hardware.radio.voice.Call.PRESENTATION_PAYPHONE;
+            default:
+                throw new RuntimeException("illegal presentation " + presentation);
+        }
+    }
+
     private static void logd(String log) {
         Rlog.d("RILUtils", log);
     }
diff --git a/src/java/com/android/internal/telephony/RadioImsProxy.java b/src/java/com/android/internal/telephony/RadioImsProxy.java
new file mode 100644
index 0000000..e3f3b16
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RadioImsProxy.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 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 android.os.RemoteException;
+import android.telephony.Rlog;
+
+/**
+ * A holder for IRadioIms.
+ * Use getAidl to get IRadioIms and call the AIDL implementations of the HAL APIs.
+ */
+public class RadioImsProxy extends RadioServiceProxy {
+    private static final String TAG = "RadioImsProxy";
+    private volatile android.hardware.radio.ims.IRadioIms mImsProxy = null;
+
+    /**
+     * Sets IRadioIms as the AIDL implementation for RadioServiceProxy.
+     * @param halVersion Radio HAL version.
+     * @param ims IRadioIms implementation.
+     */
+    public void setAidl(HalVersion halVersion, android.hardware.radio.ims.IRadioIms ims) {
+        mHalVersion = halVersion;
+        mImsProxy = ims;
+        mIsAidl = true;
+        Rlog.d(TAG, "AIDL initialized");
+    }
+
+    /**
+     * Gets the AIDL implementation of RadioImsProxy.
+     * @return IRadioIms implementation.
+     */
+    public android.hardware.radio.ims.IRadioIms getAidl() {
+        return mImsProxy;
+    }
+
+    /**
+     * Resets RadioImsProxy.
+     */
+    @Override
+    public void clear() {
+        super.clear();
+        mImsProxy = null;
+    }
+
+    /**
+     * Checks whether a RadioIms implementation exists.
+     * @return true if there is neither a HIDL nor AIDL implementation.
+     */
+    @Override
+    public boolean isEmpty() {
+        return mRadioProxy == null && mImsProxy == null;
+    }
+
+    /**
+     * No implementation in IRadioIms.
+     * @throws RemoteException.
+     */
+    @Override
+    public void responseAcknowledgement() throws RemoteException {
+        /* Currently, IRadioIms doesn't support the following response types:
+         * - RadioIndicationType.UNSOLICITED_ACK_EXP
+         * - RadioResponseType.SOLICITED_ACK_EXP */
+        // no-op
+    }
+
+    /**
+     * Calls IRadioIms#setSrvccCallInfo.
+     * @param serial Serial number of request.
+     * @param srvccCalls The list of call information.
+     * @throws RemoteException.
+     */
+    public void setSrvccCallInfo(int serial,
+            android.hardware.radio.ims.SrvccCall[] srvccCalls) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.setSrvccCallInfo(serial, srvccCalls);
+        }
+    }
+
+    /**
+     * Calls IRadioIms#updateImsRegistrationInfo.
+     * @param serial Serial number of request.
+     * @param registrationInfo The registration state information.
+     * @throws RemoteException.
+     */
+    public void updateImsRegistrationInfo(int serial,
+            android.hardware.radio.ims.ImsRegistration registrationInfo) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.updateImsRegistrationInfo(serial, registrationInfo);
+        }
+    }
+
+    /**
+     * Calls IRadioIms#startImsTraffic.
+     * @param serial Serial number of request.
+     * @throws RemoteException.
+     */
+    public void notifyImsTraffic(int serial, int token, int trafficType, boolean isStart)
+            throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.notifyImsTraffic(serial, token, trafficType, isStart);
+        }
+    }
+
+    /**
+     * Calls IRadioIms#performAcbCheck.
+     * @param serial Serial number of request.
+     * @throws RemoteException.
+     */
+    public void performAcbCheck(int serial, int token, int trafficType) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.performAcbCheck(serial, token, trafficType);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/SrvccConnection.java b/src/java/com/android/internal/telephony/SrvccConnection.java
new file mode 100644
index 0000000..b491b51
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SrvccConnection.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 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;
+
+/**
+ * Connection information for SRVCC
+ */
+public class SrvccConnection {
+    private static final String TAG = "SrvccConnection";
+
+    private static final int CALL_TYPE_NORMAL = 0;
+    private static final int CALL_TYPE_EMERGENCY = 1;
+
+    public static final int SUBSTATE_NONE = 0;
+    /** Pre-alerting state. Applicable for MT calls only */
+    public static final int SUBSTATE_PREALERTING = 1;
+
+    public static final int TONE_NONE = 0;
+    public static final int TONE_LOCAL = 1;
+    public static final int TONE_NETWORK = 2;
+
+    /** Values are CALL_TYPE_ */
+    private int mType;
+
+    /** Values are Call.State */
+    private Call.State mState;
+
+    /** Values are SUBSTATE_ */
+    private int mSubstate;
+
+    /** Values are TONE_ */
+    private int mRingbackToneType;
+
+    /** true if it is a multi-party call */
+    private boolean mIsMpty;
+
+    /** true if it is a mobile terminated call */
+    private boolean mIsMT;
+
+    /** Remote party nummber */
+    private String mNumber;
+
+    /** Values are PhoneConstants.PRESENTATION_ */
+    private int mNumPresentation;
+
+    /** Remote party name */
+    private String mName;
+
+    /** Values are PhoneConstants.PRESENTATION_ */
+    private int mNamePresentation;
+
+    public SrvccConnection(Connection c, boolean isEmergency, int ringbackToneType) {
+        mType = isEmergency ? CALL_TYPE_EMERGENCY : CALL_TYPE_NORMAL;
+        mState = c.getState();
+        mSubstate = SUBSTATE_NONE;
+        mRingbackToneType = ringbackToneType;
+        mIsMpty = false;
+        mIsMT = c.isIncoming();
+        mNumber = c.getAddress();
+        mNumPresentation = c.getNumberPresentation();
+        mName = c.getCnapName();
+        mNamePresentation = c.getCnapNamePresentation();
+    }
+
+    /** Returns the type of the call */
+    public int getType() {
+        return mType;
+    }
+
+    /** Returns the state */
+    public Call.State getState() {
+        return mState;
+    }
+
+    /** Returns the sub state */
+    public int getSubState() {
+        return mSubstate;
+    }
+
+    /** Returns the ringback tone type */
+    public int getRingbackToneType() {
+        return mRingbackToneType;
+    }
+
+    /** true if it is a multi-party call */
+    public boolean isMultiParty() {
+        return mIsMpty;
+    }
+
+    /** true if it is a mobile terminated call */
+    public boolean isIncoming() {
+        return mIsMT;
+    }
+
+    /** Returns the remote party nummber */
+    public String getNumber() {
+        return mNumber;
+    }
+
+    /** Returns the number presentation */
+    public int getNumberPresentation() {
+        return mNumPresentation;
+    }
+
+    /** Returns the remote party name */
+    public String getName() {
+        return mName;
+    }
+
+    /** Returns the name presentation */
+    public int getNamePresentation() {
+        return mNamePresentation;
+    }
+}