Merge "Ensure ImsConferenceController recalcs when a conference is no longer full." into udc-dev
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 71b2a9a..57f85ca 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -870,7 +870,7 @@
     <string name="radioInfo_lac" msgid="3892986460272607013">"LAC"</string>
     <string name="radioInfo_cid" msgid="1423185536264406705">"CID"</string>
     <string name="radio_info_subid" msgid="6839966868621703203">"Sous-identifiant actuel :"</string>
-    <string name="radio_info_dds" msgid="1122593144425697126">"Sous-identifiant de la carte SIM par défaut pour les données :"</string>
+    <string name="radio_info_dds" msgid="1122593144425697126">"Sous-identifiant SIM par défaut pour les données :"</string>
     <string name="radio_info_dl_kbps" msgid="2382922659525318726">"Bande passante de téléchargement (kbit/s) :"</string>
     <string name="radio_info_ul_kbps" msgid="2102225400904799036">"Bande passante d\'importation (kbit/s) :"</string>
     <string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Configuration de la chaîne physique LTE :"</string>
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index b49ff4e..bc1bae6 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -51,7 +51,6 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
@@ -146,11 +145,9 @@
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.satellite.ISatelliteDatagramCallback;
-import android.telephony.satellite.ISatelliteDatagramReceiverAck;
 import android.telephony.satellite.ISatellitePositionUpdateCallback;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
 import android.telephony.satellite.ISatelliteStateCallback;
-import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteDatagramCallback;
@@ -182,6 +179,7 @@
 import com.android.internal.telephony.ICallForwardingInfoCallback;
 import com.android.internal.telephony.IImsStateCallback;
 import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.ILongConsumer;
 import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.IccCard;
@@ -198,7 +196,6 @@
 import com.android.internal.telephony.ProxyController;
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.RILUtils;
 import com.android.internal.telephony.RadioInterfaceCapabilityController;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.SmsApplication;
@@ -266,7 +263,6 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
@@ -6764,7 +6760,7 @@
                 mApp, subId, "getAllowedNetworkTypesForReason");
         final long identity = Binder.clearCallingIdentity();
         try {
-            return getPhoneFromSubId(subId).getAllowedNetworkTypes(reason);
+            return getPhoneFromSubIdOrDefault(subId).getAllowedNetworkTypes(reason);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -9566,6 +9562,34 @@
     }
 
     @Override
+    public void setCarrierServicePackageOverride(
+            int subId, String carrierServicePackage, String callingPackage) {
+        TelephonyPermissions.enforceShellOnly(
+                Binder.getCallingUid(), "setCarrierServicePackageOverride");
+
+        // Verify that the callingPackage belongs to the calling UID
+        mApp.getSystemService(AppOpsManager.class)
+                .checkPackage(Binder.getCallingUid(), callingPackage);
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final Phone phone = getPhone(subId);
+            if (phone == null || phone.getSubId() != subId) {
+                loge("setCarrierServicePackageOverride fails with invalid subId: " + subId);
+                throw new IllegalArgumentException("No phone for subid");
+            }
+            CarrierPrivilegesTracker cpt = phone.getCarrierPrivilegesTracker();
+            if (cpt == null) {
+                loge("setCarrierServicePackageOverride failed with no CPT for phone");
+                throw new IllegalStateException("No CPT for phone");
+            }
+            cpt.setTestOverrideCarrierServicePackage(carrierServicePackage);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public int getCarrierIdListVersion(int subId) {
         enforceReadPrivilegedPermission("getCarrierIdListVersion");
 
@@ -12388,7 +12412,7 @@
      * This method requests modem to check if there are any pending datagrams to be received over
      * satellite. If there are any incoming datagrams, they will be received via
      * {@link SatelliteDatagramCallback#onSatelliteDatagramReceived(long, SatelliteDatagram, int,
-     *          ISatelliteDatagramReceiverAck)}
+     * ILongConsumer)})}
      *
      * @param subId The subId of the subscription used for receiving datagrams.
      * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
@@ -12409,25 +12433,23 @@
      * encoding or encryption.
      *
      * @param subId The subId of the subscription to send satellite datagrams for.
-     * @param datagramId An id that uniquely identifies datagram requested to be sent.
      * @param datagramType datagram type indicating whether the datagram is of type
      *                     SOS_SMS or LOCATION_SHARING.
      * @param datagram encoded gateway datagram which is encrypted by the caller.
      *                 Datagram will be passed down to modem without any encoding or encryption.
      * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
      *                                 full screen mode.
-     * @param result The result receiver that returns datagramId if datagram is sent successfully
-     *               or {@link SatelliteManager.SatelliteError} of the request if it is failed.
+     * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      */
     @Override
-    public void sendSatelliteDatagram(int subId, long datagramId,
-            @SatelliteManager.DatagramType int datagramType, SatelliteDatagram datagram,
-            boolean needFullScreenPointingUI, @NonNull ResultReceiver result) {
+    public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
+            @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
+            @NonNull IIntegerConsumer callback) {
         enforceSatelliteCommunicationPermission("sendSatelliteDatagram");
-        mSatelliteController.sendSatelliteDatagram(subId, datagramId, datagramType, datagram,
-                needFullScreenPointingUI, result);
+        mSatelliteController.sendSatelliteDatagram(subId, datagramType, datagram,
+                needFullScreenPointingUI, callback);
     }
 
     /**
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 5f14387..4826d2b 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -713,11 +713,11 @@
         // an emergency-only account
         String id = isEmergency ? EMERGENCY_ACCOUNT_HANDLE_ID : prefix +
                 String.valueOf(phone.getSubId());
-        return makePstnPhoneAccountHandleWithPrefix(id, prefix, isEmergency, userHandle);
+        return makePstnPhoneAccountHandleWithId(id, userHandle);
     }
 
-    public static PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(
-            String id, String prefix, boolean isEmergency, UserHandle userHandle) {
+    public static PhoneAccountHandle makePstnPhoneAccountHandleWithId(
+            String id, UserHandle userHandle) {
         ComponentName pstnConnectionServiceName = getPstnConnectionServiceName();
         // If user handle is null, resort to default constructor to use phone process's
         // user handle
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 1858fe2..14eba74 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -100,6 +100,10 @@
     private static final String DISABLE = "disable";
     private static final String QUERY = "query";
     private static final String CARRIER_RESTRICTION_STATUS_TEST = "carrier_restriction_status_test";
+    private static final String SET_CARRIER_SERVICE_PACKAGE_OVERRIDE =
+            "set-carrier-service-package-override";
+    private static final String CLEAR_CARRIER_SERVICE_PACKAGE_OVERRIDE =
+            "clear-carrier-service-package-override";
     private final String QUOTES = "\"";
 
     private static final String CALL_COMPOSER_TEST_MODE = "test-mode";
@@ -352,6 +356,10 @@
                 return handleRadioCommand();
             case CARRIER_RESTRICTION_STATUS_TEST:
                 return handleCarrierRestrictionStatusCommand();
+            case SET_CARRIER_SERVICE_PACKAGE_OVERRIDE:
+                return setCarrierServicePackageOverride();
+            case CLEAR_CARRIER_SERVICE_PACKAGE_OVERRIDE:
+                return clearCarrierServicePackageOverride();
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -3103,6 +3111,96 @@
         return result;
     }
 
+    // set-carrier-service-package-override
+    private int setCarrierServicePackageOverride() {
+        PrintWriter errPw = getErrPrintWriter();
+        int subId = SubscriptionManager.getDefaultSubscriptionId();
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-s":
+                    try {
+                        subId = Integer.parseInt(getNextArgRequired());
+                    } catch (NumberFormatException e) {
+                        errPw.println(
+                                "set-carrier-service-package-override requires an integer as a"
+                                        + " subscription ID.");
+                        return -1;
+                    }
+                    break;
+            }
+        }
+
+        String packageName = getNextArg();
+        if (packageName == null) {
+            errPw.println("set-carrier-service-package-override requires a override package name.");
+            return -1;
+        }
+
+        try {
+            mInterface.setCarrierServicePackageOverride(
+                    subId, packageName, mContext.getOpPackageName());
+
+            if (VDBG) {
+                Log.v(
+                        LOG_TAG,
+                        "set-carrier-service-package-override -s " + subId + " " + packageName);
+            }
+        } catch (RemoteException | IllegalArgumentException | IllegalStateException e) {
+            Log.w(
+                    LOG_TAG,
+                    "set-carrier-service-package-override -s "
+                            + subId
+                            + " "
+                            + packageName
+                            + ", error"
+                            + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    // clear-carrier-service-package-override
+    private int clearCarrierServicePackageOverride() {
+        PrintWriter errPw = getErrPrintWriter();
+        int subId = getDefaultSlot();
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-s":
+                    try {
+                        subId = Integer.parseInt(getNextArgRequired());
+                    } catch (NumberFormatException e) {
+                        errPw.println(
+                                "clear-carrier-service-package-override requires an integer as a"
+                                        + " subscription ID.");
+                        return -1;
+                    }
+                    break;
+            }
+        }
+
+        try {
+            mInterface.setCarrierServicePackageOverride(subId, null, mContext.getOpPackageName());
+
+            if (VDBG) {
+                Log.v(LOG_TAG, "clear-carrier-service-package-override -s " + subId);
+            }
+        } catch (RemoteException | IllegalArgumentException | IllegalStateException e) {
+            Log.w(
+                    LOG_TAG,
+                    "clear-carrier-service-package-override -s "
+                            + subId
+                            + ", error"
+                            + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
 
     /**
      * Building the string that can be used to build the JsonObject which supports to stub the data
diff --git a/src/com/android/phone/TimeConsumingPreferenceActivity.java b/src/com/android/phone/TimeConsumingPreferenceActivity.java
index d21f6a8..1fe548c 100644
--- a/src/com/android/phone/TimeConsumingPreferenceActivity.java
+++ b/src/com/android/phone/TimeConsumingPreferenceActivity.java
@@ -192,7 +192,12 @@
         if (mIsForeground) {
             showDialog(error);
         }
-        preference.setEnabled(false);
+
+        //If the error is due to RESPONSE_ERROR, do not disable the item so end user
+        //can continue to interact with it.
+        if (error != RESPONSE_ERROR) {
+            preference.setEnabled(false);
+        }
     }
 
     @Override
diff --git a/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java b/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
index 2546023..8288c43 100644
--- a/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
+++ b/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
@@ -45,7 +45,7 @@
     private static final String ENTITLEMENT_STATUS_KEY = "EntitlementStatus";
     private static final String PROVISION_STATUS_KEY = "ProvStatus";
     private static final String SERVICE_FLOW_URL_KEY = "ServiceFlow_URL";
-    private static final String PROVISION_TIME_LEFT_KEY = "ProvisionTimeLeft";
+    private static final String SERVICE_FLOW_USERDATA_KEY = "ServiceFlow_UserData";
     private static final String DEFAULT_EAP_AKA_RESPONSE = "Default EAP AKA response";
     /**
      * UUID to report an anomaly if an unexpected error is received during entitlement check.
@@ -100,8 +100,7 @@
         requestBuilder.setTerminalModel("modelY");
         requestBuilder.setTerminalSoftwareVersion("versionZ");
         requestBuilder.setAcceptContentType(ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_JSON);
-        requestBuilder.setNetworkIdentifier(
-                TelephonyManager.convertPremiumCapabilityToString(capability));
+        requestBuilder.setBoostType(getBoostTypeFromPremiumCapability(capability));
         ServiceEntitlementRequest request = requestBuilder.build();
         PremiumNetworkEntitlementResponse premiumNetworkEntitlementResponse =
                 new PremiumNetworkEntitlementResponse();
@@ -109,7 +108,7 @@
         String response = null;
         try {
             response = mServiceEntitlement.queryEntitlementStatus(
-                    ServiceEntitlement.APP_PREMIUM_NETWORK_SLICE,
+                    ServiceEntitlement.APP_DATA_PLAN_BOOST,
                     request);
         } catch (ServiceEntitlementException e) {
             Log.e(TAG, "queryEntitlementStatus failed", e);
@@ -123,10 +122,9 @@
             JSONObject jsonAuthResponse = new JSONObject(response);
             String entitlementStatus = null;
             String provisionStatus = null;
-            String provisionTimeLeft = null;
-            if (jsonAuthResponse.has(ServiceEntitlement.APP_PREMIUM_NETWORK_SLICE)) {
+            if (jsonAuthResponse.has(ServiceEntitlement.APP_DATA_PLAN_BOOST)) {
                 JSONObject jsonToken = jsonAuthResponse.getJSONObject(
-                        ServiceEntitlement.APP_PREMIUM_NETWORK_SLICE);
+                        ServiceEntitlement.APP_DATA_PLAN_BOOST);
                 if (jsonToken.has(ENTITLEMENT_STATUS_KEY)) {
                     entitlementStatus = jsonToken.getString(ENTITLEMENT_STATUS_KEY);
                     if (entitlementStatus == null) {
@@ -142,20 +140,17 @@
                                 Integer.parseInt(provisionStatus);
                     }
                 }
-                if (jsonToken.has(PROVISION_TIME_LEFT_KEY)) {
-                    provisionTimeLeft = jsonToken.getString(PROVISION_TIME_LEFT_KEY);
-                    if (provisionTimeLeft != null) {
-                        premiumNetworkEntitlementResponse.mProvisionTimeLeft =
-                                Integer.parseInt(provisionTimeLeft);
-                    }
-                }
                 if (jsonToken.has(SERVICE_FLOW_URL_KEY)) {
                     premiumNetworkEntitlementResponse.mServiceFlowURL =
                             jsonToken.getString(SERVICE_FLOW_URL_KEY);
                 }
+                if (jsonToken.has(SERVICE_FLOW_USERDATA_KEY)) {
+                    premiumNetworkEntitlementResponse.mServiceFlowUserData =
+                            jsonToken.getString(SERVICE_FLOW_USERDATA_KEY);
+                }
+            } else {
+                Log.e(TAG, "queryEntitlementStatus failed with no app");
             }
-
-
         } catch (JSONException e) {
             Log.e(TAG, "queryEntitlementStatus failed", e);
             reportAnomaly(UUID_ENTITLEMENT_CHECK_UNEXPECTED_ERROR,
@@ -194,4 +189,12 @@
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TELEPHONY,
                 BYPASS_EAP_AKA_AUTH_FOR_SLICE_PURCHASE_ENABLED, false);
     }
+
+    @NonNull private String getBoostTypeFromPremiumCapability(
+            @TelephonyManager.PremiumCapability int capability) {
+        if (capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY) {
+            return "0" /* REALTIME_INTERACTIVE_TRAFFIC */;
+        }
+        return "";
+    }
 }
diff --git a/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java b/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
index 4e63e35..242ca69 100644
--- a/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
+++ b/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
@@ -30,40 +30,39 @@
     public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING = 3;
     public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED = 4;
 
-    @IntDef(prefix = {"PREMIUM_NETWORK_ENTITLEMENT_STATUS_"},
-            value = {
-                    PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED,
-                    PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED,
-                    PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE,
-                    PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING,
-                    PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED
-            })
+    @IntDef(prefix = {"PREMIUM_NETWORK_ENTITLEMENT_STATUS_"}, value = {
+            PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED,
+            PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED,
+            PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE,
+            PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING,
+            PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED
+    })
     public @interface PremiumNetworkEntitlementStatus {}
 
     public static final int PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED = 0;
     public static final int PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED = 1;
-    public static final int PREMIUM_NETWORK_PROVISION_STATUS_NOT_REQUIRED = 2;
+    public static final int PREMIUM_NETWORK_PROVISION_STATUS_NOT_AVAILABLE = 2;
     public static final int PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS = 3;
 
-    @IntDef(prefix = {"PREMIUM_NETWORK_PROVISION_STATUS_"},
-            value = {
-                    PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED,
-                    PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED,
-                    PREMIUM_NETWORK_PROVISION_STATUS_NOT_REQUIRED,
-                    PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS
-            })
+    @IntDef(prefix = {"PREMIUM_NETWORK_PROVISION_STATUS_"}, value = {
+            PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED,
+            PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED,
+            PREMIUM_NETWORK_PROVISION_STATUS_NOT_AVAILABLE,
+            PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS
+    })
     public @interface PremiumNetworkProvisionStatus {}
 
     @PremiumNetworkEntitlementStatus public int mEntitlementStatus;
     @PremiumNetworkProvisionStatus public int mProvisionStatus;
-    public int mProvisionTimeLeft;
     @NonNull public String mServiceFlowURL;
+    @NonNull public String mServiceFlowUserData;
 
     /**
      * @return {@code true} if the premium network is provisioned and {@code false} otherwise.
      */
     public boolean isProvisioned() {
         return mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED
+                || mEntitlementStatus == PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED
                 || mEntitlementStatus == PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED;
     }
 
@@ -83,7 +82,10 @@
     public boolean isPremiumNetworkCapabilityAllowed() {
         switch (mEntitlementStatus) {
             case PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE:
-            case PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED:
+                return false;
+        }
+        switch (mProvisionStatus) {
+            case PREMIUM_NETWORK_PROVISION_STATUS_NOT_AVAILABLE:
                 return false;
         }
         return true;
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 7bbe9cf..755c85f 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -981,23 +981,25 @@
             // event package; some carriers are known to keep a disconnected participant around in
             // subsequent CEP updates with a state of disconnected, even though its no longer part
             // of the conference.
-            // Note: We consider 0 to still be a single party conference since some carriers will
-            // send a conference event package with JUST the host in it when the conference is
-            // disconnected.  We don't want to change back to conference mode prior to disconnection
-            // or we will not log the call.
-            boolean isSinglePartyConference = participants.stream()
+            final long numActiveCepParticipantsOtherThanHost = participants.stream()
                     .filter(p -> {
                         Pair<Uri, Uri> pIdent = new Pair<>(p.getHandle(), p.getEndpoint());
                         return !Objects.equals(mHostParticipantIdentity, pIdent)
                                 && p.getState() != Connection.STATE_DISCONNECTED;
                     })
-                    .count() <= 1;
+                    .count();
+            // We consider 0 to still be a single party conference since some carriers
+            // will send a conference event package with JUST the host in it when the conference
+            // is disconnected.  We don't want to change back to conference mode prior to
+            // disconnection or we will not log the call.
+            final boolean isCepForSinglePartyConference =
+                    numActiveCepParticipantsOtherThanHost <= 1;
 
             // We will only process the CEP data if:
             // 1. We're not emulating a single party call.
             // 2. We're emulating a single party call and the CEP contains more than just the
             //    single party
-            if ((!isMultiparty() && !isSinglePartyConference)
+            if ((!isMultiparty() && !isCepForSinglePartyConference)
                     || isMultiparty()) {
                 // Add any new participants and update existing.
                 for (ConferenceParticipant participant : participants) {
@@ -1083,15 +1085,17 @@
 
             int newParticipantCount = mConferenceParticipantConnections.size();
             Log.v(this, "handleConferenceParticipantsUpdate: oldParticipantCount=%d, "
-                            + "newParticipantcount=%d", oldParticipantCount, newParticipantCount);
-            // If the single party call emulation fature flag is enabled, we can potentially treat
+                            + "newParticipantCount=%d, isMultiPty=%b, cepParticipantCt=%d",
+                    oldParticipantCount, newParticipantCount, isMultiparty(),
+                    numActiveCepParticipantsOtherThanHost);
+            // If the single party call emulation feature flag is enabled, we can potentially treat
             // the conference as a single party call when there is just one participant.
             if (mFeatureFlagProxy.isUsingSinglePartyCallEmulation() &&
                     !mConferenceHost.isAdhocConferenceCall()) {
                 if (oldParticipantCount != 1 && newParticipantCount == 1) {
                     // If number of participants goes to 1, emulate a single party call.
                     startEmulatingSinglePartyCall();
-                } else if (!isMultiparty() && !isSinglePartyConference) {
+                } else if (!isMultiparty() && !isCepForSinglePartyConference) {
                     // Number of participants increased, so stop emulating a single party call.
                     stopEmulatingSinglePartyCall();
                 }
@@ -1115,8 +1119,8 @@
                     // OR if the conference had a single participant and is emulating a standalone
                     // call.
                     && (oldParticipantCount > 0 || !isMultiparty())
-                    // AND the CEP says there is nobody left any more.
-                    && newParticipantCount == 0) {
+                    // AND the CEP says there is nobody left anymore.
+                    && numActiveCepParticipantsOtherThanHost == 0) {
                 Log.i(this, "handleConferenceParticipantsUpdate: empty conference; "
                         + "local disconnect.");
                 onDisconnect();
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 6650eac..57e65ee 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -1593,9 +1593,8 @@
                         int subscriptionId = phone.getSubId();
                         Log.i(this, "setupAccounts: Phone with subscription id %d", subscriptionId);
                         // setupAccounts can be called multiple times during service changes.
-                        // Don't add an account if the Icc has not been set yet.
-                        if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)
-                                || phone.getFullIccSerialNumber() == null) {
+                        // Don't add an account if subscription is not ready.
+                        if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
                             Log.d(this, "setupAccounts: skipping invalid subid %d", subscriptionId);
                             continue;
                         }
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index f381f11..9f248b7 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -16,6 +16,10 @@
 
 package com.android.services.telephony;
 
+import static android.telephony.ims.ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED;
+import static android.telephony.ims.ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL;
+import static android.telephony.ims.ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ContentResolver;
@@ -2483,13 +2487,25 @@
                             ImsPhoneConnection imsPhoneConnection =
                                     (ImsPhoneConnection) mOriginalConnection;
                             reasonInfo = imsPhoneConnection.getImsReasonInfo();
-                            if (reasonInfo != null && reasonInfo.getCode()
-                                    == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL) {
-                                EmergencyNumber emergencyNumber =
-                                        imsPhoneConnection.getEmergencyNumberInfo();
-                                if (emergencyNumber != null) {
-                                    mEmergencyServiceCategory =
-                                            emergencyNumber.getEmergencyServiceCategoryBitmask();
+                            if (reasonInfo != null) {
+                                int reasonCode = reasonInfo.getCode();
+                                int extraCode = reasonInfo.getExtraCode();
+                                if ((reasonCode == CODE_SIP_ALTERNATE_EMERGENCY_CALL)
+                                        || (reasonCode == CODE_LOCAL_CALL_CS_RETRY_REQUIRED
+                                                && extraCode == EXTRA_CODE_CALL_RETRY_EMERGENCY)) {
+                                    EmergencyNumber numberInfo =
+                                            imsPhoneConnection.getEmergencyNumberInfo();
+                                    if (numberInfo != null) {
+                                        mEmergencyServiceCategory =
+                                                numberInfo.getEmergencyServiceCategoryBitmask();
+                                    } else {
+                                        Log.i(this, "mEmergencyServiceCategory no EmergencyNumber");
+                                    }
+
+                                    if (mEmergencyServiceCategory != null) {
+                                        Log.i(this, "mEmergencyServiceCategory="
+                                                + mEmergencyServiceCategory);
+                                    }
                                 }
                             }
                         }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 8cd0b13..7311113 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -83,6 +83,7 @@
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.internal.telephony.imsphone.ImsPhoneMmiCode;
+import com.android.internal.telephony.satellite.SatelliteSOSMessageRecommender;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.phone.FrameworksUtils;
@@ -119,6 +120,10 @@
     // Timeout before we continue with the emergency call without waiting for DDS switch response
     // from the modem.
     private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1000;
+
+    // Timeout before we terminate the outgoing DSDA call if HOLD did not complete in time on the
+    // existing call.
+    private static final int DEFAULT_DSDA_OUTGOING_CALL_HOLD_TIMEOUT_MS = 1000;
     private static final String KEY_DOMAIN_COMPARE_FEATURE_ENABLED_FLAG =
             "is_domain_selection_compare_feature_enabled";
 
@@ -205,6 +210,7 @@
     public Pair<WeakReference<TelephonyConnection>, Queue<Phone>> mEmergencyRetryCache;
     private DeviceState mDeviceState = new DeviceState();
     private EmergencyStateTracker mEmergencyStateTracker;
+    private SatelliteSOSMessageRecommender mSatelliteSOSMessageRecommender;
     private DomainSelectionResolver mDomainSelectionResolver;
     private EmergencyCallDomainSelectionConnection mEmergencyCallDomainSelectionConnection;
     private TelephonyConnection mEmergencyConnection;
@@ -568,6 +574,34 @@
                 }
             };
 
+    private final TelephonyConnection.TelephonyConnectionListener
+            mEmergencyConnectionSatelliteListener =
+            new TelephonyConnection.TelephonyConnectionListener() {
+                @Override
+                public void onStateChanged(Connection connection,
+                        @Connection.ConnectionState int state) {
+                    if (connection == null) {
+                        Log.d(this,
+                                "onStateChanged for satellite listener: connection is null");
+                        return;
+                    }
+                    if (mSatelliteSOSMessageRecommender == null) {
+                        Log.d(this, "onStateChanged for satellite listener: "
+                                + "mSatelliteSOSMessageRecommender is null");
+                        return;
+                    }
+
+                    TelephonyConnection c = (TelephonyConnection) connection;
+                    mSatelliteSOSMessageRecommender.onEmergencyCallConnectionStateChanged(
+                            c.getTelecomCallId(), state);
+                    if (state == Connection.STATE_DISCONNECTED
+                            || state == Connection.STATE_ACTIVE) {
+                        c.removeTelephonyConnectionListener(mEmergencyConnectionSatelliteListener);
+                        mSatelliteSOSMessageRecommender = null;
+                    }
+                }
+            };
+
     /**
      * A listener for calls.
      */
@@ -599,6 +633,39 @@
                 }
             };
 
+    private static class StateHoldingListener extends
+            TelephonyConnection.TelephonyConnectionListener {
+        private final CompletableFuture<Boolean> mStateHoldingFuture;
+
+        StateHoldingListener(CompletableFuture<Boolean> future) {
+            mStateHoldingFuture = future;
+        }
+
+        @Override
+        public void onStateChanged(
+                Connection connection, @Connection.ConnectionState int state) {
+            TelephonyConnection c = (TelephonyConnection) connection;
+            if (c != null) {
+                switch (c.getState()) {
+                    case Connection.STATE_HOLDING: {
+                        Log.d(LOG_TAG, "Connection " + connection
+                                + " changed to STATE_HOLDING!");
+                        mStateHoldingFuture.complete(true);
+                        c.removeTelephonyConnectionListener(this);
+                    }
+                    break;
+                    case Connection.STATE_DISCONNECTED: {
+                        Log.d(LOG_TAG, "Connection " + connection
+                                + " changed to STATE_DISCONNECTED!");
+                        mStateHoldingFuture.complete(false);
+                        c.removeTelephonyConnectionListener(this);
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
     private final DomainSelectionConnection.DomainSelectionConnectionCallback
             mEmergencyDomainSelectionConnectionCallback =
                     new DomainSelectionConnection.DomainSelectionConnectionCallback() {
@@ -1083,6 +1150,21 @@
                     }
                     return resultConnection;
                 } else {
+                    if (mTelephonyManagerProxy.isConcurrentCallsPossible()) {
+                        delayDialForOtherSubHold(phone, request.getAccountHandle(), (result) -> {
+                            Log.d(this,
+                                    "onCreateOutgoingConn - delayDialForOtherSubHold result = "
+                                            + result);
+                            if (result) {
+                                placeOutgoingConnection(request, resultConnection, phone);
+                            } else {
+                                ((TelephonyConnection) resultConnection).hangup(
+                                        android.telephony.DisconnectCause.LOCAL);
+                            }
+                        });
+                        return resultConnection;
+                    }
+                    // The standard case.
                     return placeOutgoingConnection(request, resultConnection, phone);
                 }
             } else {
@@ -1959,12 +2041,14 @@
                         }
                     });
         }
+
         final com.android.internal.telephony.Connection originalConnection;
         try {
             if (phone != null) {
                 boolean isEmergency = mTelephonyManagerProxy.isCurrentEmergencyNumber(number);
                 Log.i(this, "placeOutgoingConnection isEmergency=" + isEmergency);
                 if (isEmergency) {
+                    handleEmergencyCallStartedForSatelliteSOSMessageRecommender(connection, phone);
                     if (!getAllConnections().isEmpty()) {
                         if (!shouldHoldForEmergencyCall(phone)) {
                             // If we do not support holding ongoing calls for an outgoing
@@ -2353,10 +2437,23 @@
             return false;
         }
 
-        if (reasonInfo != null
-                && reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL) {
-            onEmergencyRedial(c, c.getPhone().getDefaultPhone());
-            return true;
+        if (reasonInfo != null) {
+            int reasonCode = reasonInfo.getCode();
+            int extraCode = reasonInfo.getExtraCode();
+            if ((reasonCode == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL)
+                    || (reasonCode == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
+                            && extraCode == ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY)) {
+                // clear normal call domain selector
+                c.removeTelephonyConnectionListener(mNormalCallConnectionListener);
+                if (mDomainSelectionConnection != null) {
+                    mDomainSelectionConnection.finishSelection();
+                    mDomainSelectionConnection = null;
+                }
+                mNormalCallConnection = null;
+
+                onEmergencyRedial(c, c.getPhone().getDefaultPhone());
+                return true;
+            }
         }
 
         return maybeReselectDomainForNormalCall(c, callFailCause, reasonInfo);
@@ -2367,14 +2464,8 @@
         Log.i(this, "maybeReselectDomainForEmergencyCall "
                 + "csCause=" +  callFailCause + ", psCause=" + reasonInfo);
 
-        // EMERGENCY_TEMP_FAILURE and EMERGENCY_PERM_FAILURE shall be handled after
-        // reselecting new {@link Phone} in {@link #retryOutgoingOriginalConnection()}.
         if (c.getOriginalConnection() != null
                 && c.getOriginalConnection().getDisconnectCause()
-                        != android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
-                && c.getOriginalConnection().getDisconnectCause()
-                        != android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE
-                && c.getOriginalConnection().getDisconnectCause()
                         != android.telephony.DisconnectCause.LOCAL
                 && c.getOriginalConnection().getDisconnectCause()
                         != android.telephony.DisconnectCause.POWER_OFF) {
@@ -2513,6 +2604,7 @@
 
         mIsEmergencyCallPending = true;
         c.addTelephonyConnectionListener(mEmergencyConnectionListener);
+        handleEmergencyCallStartedForSatelliteSOSMessageRecommender(c, phone);
 
         if (mEmergencyStateTracker == null) {
             mEmergencyStateTracker = EmergencyStateTracker.getInstance();
@@ -2683,6 +2775,12 @@
         return mEmergencyConnectionListener;
     }
 
+    @VisibleForTesting
+    public TelephonyConnection.TelephonyConnectionListener
+            getEmergencyConnectionSatelliteListener() {
+        return mEmergencyConnectionSatelliteListener;
+    }
+
     private boolean isVideoCallHoldAllowed(Phone phone) {
          CarrierConfigManager cfgManager = (CarrierConfigManager)
                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -2849,6 +2947,7 @@
         if (phone == null) {
             // Do not block indefinitely.
             completeConsumer.accept(false);
+            return;
         }
         try {
             // Waiting for PhoneSwitcher to complete the operation.
@@ -2968,6 +3067,64 @@
         return modemResultFuture;
     }
 
+    private void addTelephonyConnectionListener(Conferenceable c,
+            TelephonyConnection.TelephonyConnectionListener listener) {
+        if (c instanceof TelephonyConnection) {
+            TelephonyConnection telephonyConnection = (TelephonyConnection) c;
+            telephonyConnection.addTelephonyConnectionListener(listener);
+        } else if (c instanceof ImsConference) {
+            ImsConference imsConference = (ImsConference) c;
+            TelephonyConnection conferenceHost =
+                    (TelephonyConnection) imsConference.getConferenceHost();
+            conferenceHost.addTelephonyConnectionListener(listener);
+        } else {
+            throw new IllegalArgumentException(
+                    "addTelephonyConnectionListener(): Unexpected conferenceable! " + c);
+        }
+    }
+
+    private CompletableFuture<Boolean> listenForHoldStateChanged(
+            @NonNull Conferenceable conferenceable) {
+        CompletableFuture<Boolean> future = new CompletableFuture<>();
+        final StateHoldingListener stateHoldingListener = new StateHoldingListener(future);
+        addTelephonyConnectionListener(conferenceable, stateHoldingListener);
+        return future;
+    }
+
+    // If there are any live calls on the other subscription, sends a hold request for the live call
+    // and waits for the STATE_HOLDING confirmation, to sequence the dial of the outgoing call.
+    private void delayDialForOtherSubHold(Phone phone, PhoneAccountHandle phoneAccountHandle,
+            Consumer<Boolean> completeConsumer) {
+        Conferenceable c = maybeHoldCallsOnOtherSubs(phoneAccountHandle);
+        if (c == null) {
+            // Nothing to hold.
+            completeConsumer.accept(true);
+            return;
+        }
+
+        if (phone == null) {
+            completeConsumer.accept(false);
+            return;
+        }
+
+        try {
+            // We have dispatched a 'hold' command to a live call (Connection or Conference) on the
+            // other sub. Listen to state changed events to see if this entered hold state.
+            CompletableFuture<Boolean> stateHoldingFuture = listenForHoldStateChanged(c);
+            // a timeout that will complete the future to not block the outgoing call indefinitely.
+            CompletableFuture<Boolean> timeout = new CompletableFuture<>();
+            phone.getContext().getMainThreadHandler().postDelayed(
+                    () -> timeout.complete(false), DEFAULT_DSDA_OUTGOING_CALL_HOLD_TIMEOUT_MS);
+            // Ensure that the Consumer is completed on the main thread.
+            stateHoldingFuture.acceptEitherAsync(timeout, completeConsumer,
+                    phone.getContext().getMainExecutor());
+        } catch (Exception e) {
+            Log.w(this, "delayDialForOtherSubHold - exception= "
+                    + e.getMessage());
+            completeConsumer.accept(false);
+        }
+    }
+
     /**
      * Get the Phone to use for an emergency call of the given emergency number address:
      *  a) If there are multiple Phones with the Subscriptions that support the emergency number
@@ -3585,7 +3742,20 @@
                 });
     }
 
-    private static void onUnhold(Conferenceable conferenceable) {
+    static void onHold(Conferenceable conferenceable) {
+        if (conferenceable instanceof Connection) {
+            Connection connection = (Connection) conferenceable;
+            connection.onHold();
+        } else if (conferenceable instanceof Conference) {
+            Conference conference = (Conference) conferenceable;
+            conference.onHold();
+        } else {
+            throw new IllegalArgumentException(
+                    "onHold(): Unexpected conferenceable! " + conferenceable);
+        }
+    }
+
+    static void onUnhold(Conferenceable conferenceable) {
         if (conferenceable instanceof Connection) {
             Connection connection = (Connection) conferenceable;
             connection.onUnhold();
@@ -3593,40 +3763,28 @@
             Conference conference = (Conference) conferenceable;
             conference.onUnhold();
         } else {
-            throw new IllegalArgumentException("Unexpected conferenceable! " + conferenceable);
+            throw new IllegalArgumentException(
+                    "onUnhold(): Unexpected conferenceable! " + conferenceable);
         }
     }
 
-    /**
-     * Where there are ongoing calls on multiple subscriptions for DSDA devices, let the 'hold'
-     * button perform an unhold on the other sub's Connection or Conference. This covers for Dialer
-     * apps that may not have a dedicated 'swap' button for calls across different subs.
-     * @param incomingHandle The incoming {@link PhoneAccountHandle}.
-     */
-    public void maybeUnholdCallsOnOtherSubs(@NonNull PhoneAccountHandle incomingHandle) {
-        Log.i(this, "maybeUnholdCallsOnOtherSubs: check for calls not on %s",
-                incomingHandle);
-        maybeUnholdCallsOnOtherSubs(getAllConnections(), getAllConferences(), incomingHandle,
-                mTelephonyManagerProxy);
-    }
-
-    /**
-     * Used by {@link #maybeUnholdCallsOnOtherSubs(PhoneAccountHandle)} to evaluate whether and on
-     * which connection / conference to call onUnhold(). This method exists as a convenience so that
-     * it is possible to unit test the core functionality.
+     /**
+     * Evaluates whether a connection or conference exists on subscriptions other than the one
+     * corresponding to the existing {@link PhoneAccountHandle}.
      * @param connections all individual connections, including conference participants.
      * @param conferences all conferences.
-     * @param incomingHandle the incoming handle.
+     * @param currentHandle the existing call handle;
      * @param telephonyManagerProxy the proxy to the {@link TelephonyManager} instance.
      */
-    @VisibleForTesting
-    public static void maybeUnholdCallsOnOtherSubs(@NonNull Collection<Connection> connections,
+    private static @Nullable Conferenceable maybeGetFirstConferenceableFromOtherSubscription(
+            @NonNull Collection<Connection> connections,
             @NonNull Collection<Conference> conferences,
-            @NonNull PhoneAccountHandle incomingHandle,
+            @NonNull PhoneAccountHandle currentHandle,
             TelephonyManagerProxy telephonyManagerProxy) {
         if (!telephonyManagerProxy.isConcurrentCallsPossible()) {
-            return;
+            return null;
         }
+
         List<Conference> otherSubConferences = conferences.stream()
                 .filter(c ->
                         // Exclude multiendpoint calls as they're not on this device.
@@ -3634,11 +3792,10 @@
                                 & Connection.PROPERTY_IS_EXTERNAL_CALL) == 0
                                 // Include any conferences not on same sub as current connection.
                                 && !Objects.equals(c.getPhoneAccountHandle(),
-                                incomingHandle))
+                                currentHandle))
                 .toList();
         if (!otherSubConferences.isEmpty()) {
-            onUnhold(otherSubConferences.get(0));
-            return;
+            return otherSubConferences.get(0);
         }
 
         // Considers Connections (including conference participants) only if no conferences.
@@ -3648,15 +3805,91 @@
                         (c.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) == 0
                                 // Include any calls not on same sub as current connection.
                                 && !Objects.equals(c.getPhoneAccountHandle(),
-                                incomingHandle)).toList();
+                                currentHandle)).toList();
 
         if (!otherSubConnections.isEmpty()) {
             if (otherSubConnections.size() > 1) {
-                Log.w(LOG_TAG, "Unexpected number of conferenceables: "
+                Log.w(LOG_TAG, "Unexpected number of connections: "
                         + otherSubConnections.size() + " on other sub!");
             }
-            onUnhold(otherSubConnections.get(0));
+            return otherSubConnections.get(0);
         }
+        return null;
+    }
+
+    /**
+     * Where there are ongoing calls on multiple subscriptions for DSDA devices, let the 'hold'
+     * button perform an unhold on the other sub's Connection or Conference. This covers for Dialer
+     * apps that may not have a dedicated 'swap' button for calls across different subs.
+     * @param currentHandle The {@link PhoneAccountHandle} of the current active voice call.
+     */
+    public void maybeUnholdCallsOnOtherSubs(
+            @NonNull PhoneAccountHandle currentHandle) {
+        Log.i(this, "maybeUnholdCallsOnOtherSubs: check for calls not on %s",
+                currentHandle);
+        maybeUnholdCallsOnOtherSubs(getAllConnections(), getAllConferences(),
+                currentHandle, mTelephonyManagerProxy);
+    }
+
+    /**
+     * Where there are ongoing calls on multiple subscriptions for DSDA devices, let the 'hold'
+     * button perform an unhold on the other sub's Connection or Conference. This is a convenience
+     * method to unit test the core functionality.
+     *
+     * @param connections all individual connections, including conference participants.
+     * @param conferences all conferences.
+     * @param currentHandle The {@link PhoneAccountHandle} of the current active call.
+     * @param telephonyManagerProxy the proxy to the {@link TelephonyManager} instance.
+     */
+    @VisibleForTesting
+    protected static void maybeUnholdCallsOnOtherSubs(@NonNull Collection<Connection> connections,
+            @NonNull Collection<Conference> conferences,
+            @NonNull PhoneAccountHandle currentHandle,
+            TelephonyManagerProxy telephonyManagerProxy) {
+        Conferenceable c = maybeGetFirstConferenceableFromOtherSubscription(
+                connections, conferences, currentHandle, telephonyManagerProxy);
+        if (c != null) {
+            onUnhold(c);
+        }
+    }
+
+    /**
+     * For DSDA devices, when an outgoing call is dialed out from the 2nd sub, holds the first call.
+     *
+     * @param outgoingHandle The outgoing {@link PhoneAccountHandle}.
+     * @return the Conferenceable representing the Connection or Conference to be held.
+     */
+    private @Nullable Conferenceable maybeHoldCallsOnOtherSubs(
+            @NonNull PhoneAccountHandle outgoingHandle) {
+        Log.i(this, "maybeHoldCallsOnOtherSubs: check for calls not on %s",
+                outgoingHandle);
+        return maybeHoldCallsOnOtherSubs(getAllConnections(), getAllConferences(),
+                outgoingHandle, mTelephonyManagerProxy);
+    }
+
+    /**
+     * For DSDA devices, when an outgoing call is dialed out from the 2nd sub, holds the first call.
+     * This is a convenience method to unit test the core functionality.
+     *
+     * @param connections all individual connections, including conference participants.
+     * @param conferences all conferences.
+     * @param outgoingHandle The outgoing {@link PhoneAccountHandle}.
+     * @param telephonyManagerProxy the proxy to the {@link TelephonyManager} instance.
+     * @return the {@link Conferenceable} representing the Connection or Conference to be held.
+     */
+    @VisibleForTesting
+    protected static @Nullable Conferenceable maybeHoldCallsOnOtherSubs(
+            @NonNull Collection<Connection> connections,
+            @NonNull Collection<Conference> conferences,
+            @NonNull PhoneAccountHandle outgoingHandle,
+            TelephonyManagerProxy telephonyManagerProxy) {
+        Conferenceable c = maybeGetFirstConferenceableFromOtherSubscription(
+                connections, conferences, outgoingHandle, telephonyManagerProxy);
+        if (c != null) {
+            onHold(c);
+            return c;
+        }
+        return null;
     }
 
     private void disconnectAllCallsOnOtherSubs (@NonNull PhoneAccountHandle handle) {
@@ -3698,4 +3931,14 @@
         }
         return NetworkRegistrationInfo.DOMAIN_UNKNOWN;
     }
+
+    private void handleEmergencyCallStartedForSatelliteSOSMessageRecommender(
+            @NonNull TelephonyConnection connection, @NonNull Phone phone) {
+        if (mSatelliteSOSMessageRecommender == null) {
+            mSatelliteSOSMessageRecommender = new SatelliteSOSMessageRecommender(
+                    phone.getContext().getMainLooper());
+        }
+        connection.addTelephonyConnectionListener(mEmergencyConnectionSatelliteListener);
+        mSatelliteSOSMessageRecommender.onEmergencyCallStarted(connection, phone);
+    }
 }
diff --git a/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java b/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java
new file mode 100644
index 0000000..f1bb78c
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2023 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.services.telephony.domainselection;
+
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.REDIAL_TIMER_DISABLED;
+import static android.telephony.PreciseDisconnectCause.EMERGENCY_PERM_FAILURE;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.SystemProperties;
+import android.telephony.Annotation.PreciseDisconnectCauses;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
+import java.util.ArrayList;
+
+/** Controls the cross stack redialing. */
+public class CrossSimRedialingController extends Handler {
+    private static final String TAG = "CrossSimRedialingCtrl";
+    private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1);
+    private static final int LOG_SIZE = 50;
+
+    /** An interface of a helper to check emergency number. */
+    public interface EmergencyNumberHelper {
+        /**
+         * Returns whether the number is an emergency number in the given modem slot.
+         *
+         * @param slotId The slot id to be checked.
+         * @param number The number.
+         * @return {@code true} if the number is an emergency number in the given slot.
+         */
+        boolean isEmergencyNumber(int slotId, String number);
+    }
+
+    @VisibleForTesting
+    public static final int MSG_CROSS_STACK_TIMEOUT = 1;
+    @VisibleForTesting
+    public static final int MSG_QUICK_CROSS_STACK_TIMEOUT = 2;
+
+    private static final LocalLog sLocalLog = new LocalLog(LOG_SIZE);
+
+    private final ArrayList<Integer> mStackSelectionHistory = new ArrayList<>();
+    private final ArrayList<Integer> mPermanentRejectedSlots = new ArrayList<>();
+    private final TelephonyManager mTelephonyManager;
+
+    private EmergencyNumberHelper mEmergencyNumberHelper = new EmergencyNumberHelper() {
+        @Override
+        public boolean isEmergencyNumber(int slotId, String number) {
+            // TODO(b/258112541) Add System api to check emergency number per subscription.
+            try {
+                Phone phone = PhoneFactory.getPhone(slotId);
+                if (phone != null
+                        && phone.getEmergencyNumberTracker() != null
+                        && phone.getEmergencyNumberTracker().isEmergencyNumber(number)) {
+                    return true;
+                }
+            } catch (IllegalStateException e) {
+                loge("isEmergencyNumber e=" + e);
+            }
+            return false;
+        }
+    };
+
+    private int mModemCount;
+
+    /** A cache of the carrier config {@link #KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT}. */
+    private int mCrossStackTimer;
+    /** A cache of the carrier config {@link #KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT}. */
+    private int mQuickCrossStackTimer;
+    /**
+     * A cache of the carrier config
+     * {@link #KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL}.
+     */
+    private boolean mStartQuickCrossStackTimerWhenInService;
+
+    private String mCallId;
+    private EmergencyCallDomainSelector mSelector;
+    private String mNumber;
+    private int mSlotId;
+    private int mSubId;
+
+    /**
+     * Creates an instance.
+     *
+     * @param context The Context this is associated with.
+     * @param looper The Looper to run the CrossSimRedialingController.
+     */
+    public CrossSimRedialingController(@NonNull Context context, @NonNull Looper looper) {
+        super(looper);
+
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
+    }
+
+    /** For unit test only */
+    @VisibleForTesting
+    public CrossSimRedialingController(@NonNull Context context, @NonNull Looper looper,
+            EmergencyNumberHelper emergencyNumberHelper) {
+        this(context, looper);
+
+        mEmergencyNumberHelper = emergencyNumberHelper;
+    }
+
+    /**
+     * Starts the timer.
+     *
+     * @param context The Context this is associated with.
+     * @param selector The instance of {@link EmergencyCallDomainSelector}.
+     * @param callId The call identifier.
+     * @param number The dialing number.
+     * @param inService Indiates that normal service is available.
+     * @param roaming Indicates that it's in roaming or non-domestic network.
+     * @param modemCount The number of active modem count
+     */
+    public void startTimer(@NonNull Context context,
+            @NonNull EmergencyCallDomainSelector selector,
+            @NonNull String callId, @NonNull String number,
+            boolean inService, boolean roaming, int modemCount) {
+        logi("startTimer callId=" + callId
+                + ", in service=" + inService + ", roaming=" + roaming);
+
+        if (!TextUtils.equals(mCallId, callId)) {
+            logi("startTimer callId changed");
+            mCallId = callId;
+            mStackSelectionHistory.clear();
+            mPermanentRejectedSlots.clear();
+        }
+        mSelector = selector;
+        mSlotId = selector.getSlotId();
+        mSubId = selector.getSubId();
+        mNumber = number;
+        mModemCount = modemCount;
+
+        updateCarrierConfiguration(context);
+
+        boolean firstAttempt = !mStackSelectionHistory.contains(mSlotId);
+        logi("startTimer slot=" + mSlotId + ", firstAttempt=" + firstAttempt);
+        mStackSelectionHistory.add(mSlotId);
+
+        if (firstAttempt && mQuickCrossStackTimer > REDIAL_TIMER_DISABLED && !roaming) {
+            if (inService || !mStartQuickCrossStackTimerWhenInService) {
+                logi("startTimer quick timer started");
+                sendEmptyMessageDelayed(MSG_QUICK_CROSS_STACK_TIMEOUT,
+                        mQuickCrossStackTimer);
+                return;
+            }
+        }
+
+        if (mCrossStackTimer > REDIAL_TIMER_DISABLED) {
+            logi("startTimer timer started");
+            sendEmptyMessageDelayed(MSG_CROSS_STACK_TIMEOUT, mCrossStackTimer);
+        }
+    }
+
+    /** Stops the timers. */
+    public void stopTimer() {
+        logi("stopTimer");
+        removeMessages(MSG_CROSS_STACK_TIMEOUT);
+        removeMessages(MSG_QUICK_CROSS_STACK_TIMEOUT);
+    }
+
+    /**
+     * Informs the call failure.
+     * @param cause The call failure cause.
+     */
+    public void notifyCallFailure(@PreciseDisconnectCauses int cause) {
+        logi("notifyCallFailure cause=" + cause);
+        if (cause == EMERGENCY_PERM_FAILURE) {
+            mPermanentRejectedSlots.add(mSlotId);
+        }
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch(msg.what) {
+            case MSG_CROSS_STACK_TIMEOUT:
+            case MSG_QUICK_CROSS_STACK_TIMEOUT:
+                handleCrossStackTimeout();
+                break;
+            default:
+                super.handleMessage(msg);
+                break;
+        }
+    }
+
+    private void handleCrossStackTimeout() {
+        logi("handleCrossStackTimeout");
+
+        if (isThereOtherSlot()) {
+            mSelector.notifyCrossStackTimerExpired();
+        }
+    }
+
+    /**
+     * Returns whether there is another slot emergency capable.
+     *
+     * @return {@code true} if there is another slot emergency capable,
+     *         {@code false} otherwise.
+     */
+    public boolean isThereOtherSlot() {
+        logi("isThereOtherSlot modemCount=" + mModemCount);
+        if (mModemCount < 2) return false;
+
+        for (int i = 0; i < mModemCount; i++) {
+            if (i == mSlotId) continue;
+
+            if (mPermanentRejectedSlots.contains(i)) {
+                logi("isThereOtherSlot index=" + i + ", permanent rejected");
+                continue;
+            }
+
+            int simState = mTelephonyManager.getSimState(i);
+            if (simState != TelephonyManager.SIM_STATE_READY) {
+                logi("isThereOtherSlot index=" + i + ", simState=" + simState);
+                continue;
+            }
+
+            if (mEmergencyNumberHelper.isEmergencyNumber(i, mNumber)) {
+                logi("isThereOtherSlot index=" + i + ", found");
+                return true;
+            } else {
+                logi("isThereOtherSlot index=" + i + ", not emergency number");
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Caches the configuration.
+     */
+    private void updateCarrierConfiguration(Context context) {
+        CarrierConfigManager configMgr = context.getSystemService(CarrierConfigManager.class);
+        PersistableBundle b = configMgr.getConfigForSubId(mSubId,
+                KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT,
+                KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT,
+                KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL);
+        if (b == null) {
+            b = CarrierConfigManager.getDefaultConfig();
+        }
+
+        mCrossStackTimer = b.getInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT) * 1000;
+        mQuickCrossStackTimer =
+                b.getInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT) * 1000;
+        mStartQuickCrossStackTimerWhenInService =
+                b.getBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL);
+
+        logi("updateCarrierConfiguration "
+                + ", crossStackTimer=" + mCrossStackTimer
+                + ", quickCrossStackTimer=" + mQuickCrossStackTimer
+                + ", startQuickTimerInService=" + mStartQuickCrossStackTimerWhenInService);
+    }
+
+    /** Destroys the instance. */
+    public void destroy() {
+        if (DBG) logd("destroy");
+
+        removeMessages(MSG_CROSS_STACK_TIMEOUT);
+        removeMessages(MSG_QUICK_CROSS_STACK_TIMEOUT);
+    }
+
+    private void logd(String s) {
+        Log.d(TAG, "[" + mSlotId + "|" + mSubId + "] " + s);
+    }
+
+    private void logi(String s) {
+        Log.i(TAG, "[" + mSlotId + "|" + mSubId + "] " + s);
+        sLocalLog.log(s);
+    }
+
+    private void loge(String s) {
+        Log.e(TAG, "[" + mSlotId + "|" + mSubId + "] " + s);
+        sLocalLog.log(s);
+    }
+}
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
index e2800a3..b1c288c 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -50,6 +50,8 @@
 import static android.telephony.CarrierConfigManager.ImsWfc.KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL;
 import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
 import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+import static android.telephony.PreciseDisconnectCause.EMERGENCY_PERM_FAILURE;
+import static android.telephony.PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -194,6 +196,9 @@
     private boolean mIsScanRequested = false;
     /** Indicates whether selected domain has been notified. */
     private boolean mDomainSelected = false;
+    /** Indicates whether the cross sim redialing timer has expired. */
+    private boolean mCrossStackTimerExpired = false;
+
     /**
      * Indicates whether {@link #selectDomain(SelectionAttributes, TransportSelectionCallback)}
      * is called or not.
@@ -201,11 +206,13 @@
     private boolean mDomainSelectionRequested = false;
 
     private final PowerManager.WakeLock mPartialWakeLock;
+    private final CrossSimRedialingController mCrossSimRedialingController;
 
     /** Constructor. */
     public EmergencyCallDomainSelector(Context context, int slotId, int subId,
             @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
-            @NonNull DestroyListener destroyListener) {
+            @NonNull DestroyListener destroyListener,
+            @NonNull CrossSimRedialingController csrController) {
         super(context, slotId, subId, looper, imsStateTracker, destroyListener, TAG);
 
         mImsStateTracker.addBarringInfoListener(this);
@@ -214,6 +221,7 @@
         PowerManager pm = context.getSystemService(PowerManager.class);
         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
+        mCrossSimRedialingController = csrController;
         acquireWakeLock();
     }
 
@@ -320,6 +328,23 @@
     private void reselectDomain() {
         logi("reselectDomain tryCsWhenPsFails=" + mTryCsWhenPsFails);
 
+        int cause = mSelectionAttributes.getCsDisconnectCause();
+        mCrossSimRedialingController.notifyCallFailure(cause);
+
+        // TODO(b/258112541) make EMERGENCY_PERM_FAILURE and EMERGENCY_TEMP_FAILURE public api
+        if (cause == EMERGENCY_PERM_FAILURE
+                || cause == EMERGENCY_TEMP_FAILURE) {
+            logi("reselectDomain should redial on the other subscription");
+            terminateSelectionForCrossSimRedialing(cause == EMERGENCY_PERM_FAILURE);
+            return;
+        }
+
+        if (mCrossStackTimerExpired) {
+            logi("reselectDomain cross stack timer expired");
+            terminateSelectionForCrossSimRedialing(false);
+            return;
+        }
+
         if (mIsTestEmergencyNumber) {
             selectDomainForTestEmergencyNumber();
             return;
@@ -389,6 +414,7 @@
         logi("startDomainSelection modemCount=" + mModemCount);
         updateCarrierConfiguration();
         mDomainSelectionRequested = true;
+        startCrossStackTimer();
         if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
             selectDomain();
         } else {
@@ -1135,7 +1161,7 @@
         mDomainSelected = true;
         mLastTransportType = TRANSPORT_TYPE_WLAN;
         mVoWifiTrialCount++;
-        mTransportSelectorCallback.onWlanSelected();
+        mTransportSelectorCallback.onWlanSelected(mVoWifiOverEmergencyPdn);
         mWwanSelectorCallback = null;
     }
 
@@ -1166,7 +1192,8 @@
         if (accessNetworkType == EUTRAN || accessNetworkType == NGRAN) {
             domain = NetworkRegistrationInfo.DOMAIN_PS;
         }
-        mWwanSelectorCallback.onDomainSelected(domain);
+        mWwanSelectorCallback.onDomainSelected(domain,
+                (domain == NetworkRegistrationInfo.DOMAIN_PS));
     }
 
     /**
@@ -1216,7 +1243,10 @@
             int simState = tm.getSimState(getSlotId());
             if (simState != TelephonyManager.SIM_STATE_READY) {
                 logi("allowEmergencyCalls not ready, simState=" + simState + ", iso=" + iso);
-                return false;
+                if (mCrossSimRedialingController.isThereOtherSlot()) {
+                    return false;
+                }
+                logi("allowEmergencyCalls there is no other slot available");
             }
         }
 
@@ -1225,7 +1255,18 @@
 
     private void terminateSelectionPermanentlyForSlot() {
         logi("terminateSelectionPermanentlyForSlot");
-        mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.EMERGENCY_PERM_FAILURE);
+        terminateSelection(true);
+    }
+
+    private void terminateSelectionForCrossSimRedialing(boolean permanent) {
+        logi("terminateSelectionForCrossSimRedialing perm=" + permanent);
+        terminateSelection(permanent);
+    }
+
+    private void terminateSelection(boolean permanent) {
+        mTransportSelectorCallback.onSelectionTerminated(permanent
+                ? DisconnectCause.EMERGENCY_PERM_FAILURE
+                : DisconnectCause.EMERGENCY_TEMP_FAILURE);
 
         if (mIsScanRequested && mCancelSignal != null) {
             mCancelSignal.cancel();
@@ -1233,6 +1274,41 @@
         }
     }
 
+    /** Starts the cross stack timer. */
+    public void startCrossStackTimer() {
+        boolean inService = false;
+        boolean inRoaming = false;
+
+        if (mModemCount == 1) return;
+
+        EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+        if (regResult != null) {
+            int regState = regResult.getRegState();
+
+            if ((regResult.getDomain() > 0)
+                    && (regState == REGISTRATION_STATE_HOME
+                            || regState == REGISTRATION_STATE_ROAMING)) {
+                inService = true;
+            }
+            inRoaming = (regState == REGISTRATION_STATE_ROAMING) || isInRoaming();
+        }
+
+        mCrossSimRedialingController.startTimer(mContext, this, mSelectionAttributes.getCallId(),
+                mSelectionAttributes.getNumber(), inService, inRoaming, mModemCount);
+    }
+
+    /** Notifies that the cross stack redilaing timer has been expired. */
+    public void notifyCrossStackTimerExpired() {
+        logi("notifyCrossStackTimerExpired");
+
+        mCrossStackTimerExpired = true;
+        if (mDomainSelected) {
+            // When reselecting domain, terminateSelection will be called.
+            return;
+        }
+        terminateSelectionForCrossSimRedialing(false);
+    }
+
     private static String arrayToString(int[] intArray, IntFunction<String> func) {
         int length = intArray.length;
         StringBuilder sb = new StringBuilder("{");
@@ -1303,6 +1379,7 @@
     public void destroy() {
         if (DBG) logd("destroy");
 
+        mCrossSimRedialingController.stopTimer();
         releaseWakeLock();
 
         mDestroyed = true;
diff --git a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
index 3da0044..f176d90 100644
--- a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
@@ -168,7 +168,7 @@
         mStopDomainSelection = true;
         if (mImsStateTracker.isImsRegisteredOverWlan()) {
             logd("WLAN selected");
-            mTransportSelectorCallback.onWlanSelected();
+            mTransportSelectorCallback.onWlanSelected(false);
         } else {
             if (mWwanSelectorCallback == null) {
                 mTransportSelectorCallback.onWwanSelected((callback) -> {
@@ -184,7 +184,7 @@
     private void notifyPsSelectedInternal() {
         if (mWwanSelectorCallback != null) {
             logd("notifyPsSelected - onWwanSelected");
-            mWwanSelectorCallback.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS);
+            mWwanSelectorCallback.onDomainSelected(NetworkRegistrationInfo.DOMAIN_PS, false);
         } else {
             loge("wwanSelectorCallback is null");
             mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.OUTGOING_FAILURE);
@@ -207,7 +207,7 @@
     private void notifyCsSelectedInternal() {
         if (mWwanSelectorCallback != null) {
             logd("wwanSelectorCallback -> onDomainSelected(DOMAIN_CS)");
-            mWwanSelectorCallback.onDomainSelected(NetworkRegistrationInfo.DOMAIN_CS);
+            mWwanSelectorCallback.onDomainSelected(NetworkRegistrationInfo.DOMAIN_CS, false);
         } else {
             loge("wwanSelectorCallback is null");
             mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.OUTGOING_FAILURE);
diff --git a/src/com/android/services/telephony/domainselection/SmsDomainSelector.java b/src/com/android/services/telephony/domainselection/SmsDomainSelector.java
index e7c9ac5..6930b13 100644
--- a/src/com/android/services/telephony/domainselection/SmsDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/SmsDomainSelector.java
@@ -178,7 +178,7 @@
 
     protected void notifyWlanSelected() {
         logi("DomainSelected: WLAN");
-        mTransportSelectorCallback.onWlanSelected();
+        mTransportSelectorCallback.onWlanSelected(false);
         setDomainSelectionRequested(false);
     }
 
@@ -199,7 +199,7 @@
         logi("DomainSelected: WWAN/" + DomainSelectionService.getDomainName(domain));
 
         if (mWwanSelectorCallback != null) {
-            mWwanSelectorCallback.onDomainSelected(domain);
+            mWwanSelectorCallback.onDomainSelected(domain, false);
         } else {
             mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.LOCAL);
         }
diff --git a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
index 13db06b..3a8fc86 100644
--- a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
+++ b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
@@ -70,7 +70,8 @@
         DomainSelectorBase create(Context context, int slotId, int subId,
                 @SelectorType int selectorType, boolean isEmergency, @NonNull Looper looper,
                 @NonNull ImsStateTracker imsStateTracker,
-                @NonNull DomainSelectorBase.DestroyListener listener);
+                @NonNull DomainSelectorBase.DestroyListener listener,
+                @NonNull CrossSimRedialingController crossSimRedialingController);
     }
 
     private static final class DefaultDomainSelectorFactory implements DomainSelectorFactory {
@@ -78,7 +79,8 @@
         public DomainSelectorBase create(Context context, int slotId, int subId,
                 @SelectorType int selectorType, boolean isEmergency, @NonNull Looper looper,
                 @NonNull ImsStateTracker imsStateTracker,
-                @NonNull DomainSelectorBase.DestroyListener listener) {
+                @NonNull DomainSelectorBase.DestroyListener listener,
+                @NonNull CrossSimRedialingController crossSimRedialingController) {
             DomainSelectorBase selector = null;
 
             logi("create-DomainSelector: slotId=" + slotId + ", subId=" + subId
@@ -89,7 +91,7 @@
                 case SELECTOR_TYPE_CALLING:
                     if (isEmergency) {
                         selector = new EmergencyCallDomainSelector(context, slotId, subId, looper,
-                                imsStateTracker, listener);
+                                imsStateTracker, listener, crossSimRedialingController);
                     } else {
                         selector = new NormalCallDomainSelector(context, slotId, subId, looper,
                                 imsStateTracker, listener);
@@ -192,6 +194,7 @@
     private final ImsStateTrackerFactory mImsStateTrackerFactory;
     private final DomainSelectorFactory mDomainSelectorFactory;
     private Handler mServiceHandler;
+    private CrossSimRedialingController mCrossSimRedialingController;
 
     public TelephonyDomainSelectionService(Context context) {
         this(context, ImsStateTracker::new, new DefaultDomainSelectorFactory());
@@ -221,6 +224,8 @@
             loge("Adding OnSubscriptionChangedListener failed");
         }
 
+        mCrossSimRedialingController = new CrossSimRedialingController(context, getLooper());
+
         logi("TelephonyDomainSelectionService created");
     }
 
@@ -258,6 +263,11 @@
             sm.removeOnSubscriptionsChangedListener(mSubscriptionsChangedListener);
         }
 
+        if (mCrossSimRedialingController != null) {
+            mCrossSimRedialingController.destroy();
+            mCrossSimRedialingController = null;
+        }
+
         if (mServiceHandler != null) {
             mServiceHandler.getLooper().quit();
             mServiceHandler = null;
@@ -279,7 +289,8 @@
         final boolean isEmergency = attr.isEmergency();
         ImsStateTracker ist = getImsStateTracker(slotId);
         DomainSelectorBase selector = mDomainSelectorFactory.create(mContext, slotId, subId,
-                selectorType, isEmergency, getLooper(), ist, mDestroyListener);
+                selectorType, isEmergency, getLooper(), ist, mDestroyListener,
+                mCrossSimRedialingController);
 
         if (selector != null) {
             // Ensures that ImsStateTracker is started before selecting the domain if not started
diff --git a/testapps/TestRcsApp/TestApp/lint-baseline.xml b/testapps/TestRcsApp/TestApp/lint-baseline.xml
new file mode 100644
index 0000000..8971388
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/lint-baseline.xml
@@ -0,0 +1,829 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#bootstrapAuthenticationRequest`"
+        errorLine1="            telephonyManager.bootstrapAuthenticationRequest(mUiccType,"
+        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="130"
+            column="30"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#build`"
+        errorLine1="            UaSecurityProtocolIdentifier spId = builder.build();"
+        errorLine2="                                                        ~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="129"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setOrg`"
+        errorLine1="                builder.setOrg(mOrganization)"
+        errorLine2="                        ~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="122"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setProtocol`"
+        errorLine1="                        .setProtocol(mProtocol)"
+        errorLine2="                         ~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="123"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setTlsCipherSuite`"
+        errorLine1="                        .setTlsCipherSuite(Integer.parseInt(mTlsCs.getText().toString()));"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="124"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
+        errorLine1="                    Set&lt;String&gt; registeredFt = registrationState.getRegisteredFeatureTags();"
+        errorLine2="                                                                 ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="151"
+            column="66"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getFeatureTag`"
+        errorLine1='                        stringBuilder.append(featureTagState.getFeatureTag()).append(" ").append('
+        errorLine2="                                                             ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="148"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getState`"
+        errorLine1="                                featureTagState.getState());"
+        errorLine2="                                                ~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="149"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ImsManager#getSipDelegateManager`"
+        errorLine1="            mSipDelegateManager = imsManager.getSipDelegateManager(mDefaultSmsSubId);"
+        errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="220"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
+        errorLine1="                    boolean capable = mProvisioningManager.isRcsVolteSingleRegistrationCapable();"
+        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="204"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
+        errorLine1="            mProvisioningManager.isRcsVolteSingleRegistrationCapable();"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="166"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
+        errorLine1="                    mProvisioningManager.registerRcsProvisioningCallback(mExecutorService,"
+        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="181"
+            column="42"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
+        errorLine1="                mProvisioningManager.registerRcsProvisioningCallback(getMainExecutor(), mCallback);"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+            line="221"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
+        errorLine1="                    mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());"
+        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="180"
+            column="42"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
+        errorLine1="                mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+            line="220"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+        errorLine1="                mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="195"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+        errorLine1="            mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+            line="348"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+        errorLine1="            mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="152"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getContactUri`"
+        errorLine1="                b.append(t.getContactUri());"
+        errorLine2="                           ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="220"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceCapabilities`"
+        errorLine1="                            t.getServiceCapabilities();"
+        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="227"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceCapabilities`"
+        errorLine1="                if (t.getServiceCapabilities() != null) {"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="225"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceId`"
+        errorLine1="                b.append(t.getServiceId());"
+        errorLine2="                           ~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="222"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceVersion`"
+        errorLine1="                b.append(t.getServiceVersion());"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="224"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#getSupportedDuplexModes`"
+        errorLine1="                    b.append(servCaps.getSupportedDuplexModes());"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="233"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#getUnsupportedDuplexModes`"
+        errorLine1="                    b.append(servCaps.getUnsupportedDuplexModes());"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="235"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#isAudioCapable`"
+        errorLine1="                    b.append(servCaps.isAudioCapable());"
+        errorLine2="                                      ~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="229"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#isVideoCapable`"
+        errorLine1="                    b.append(servCaps.isVideoCapable());"
+        errorLine2="                                      ~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="231"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getCapabilityMechanism`"
+        errorLine1="        if (c.getCapabilityMechanism() == RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE) {"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="216"
+            column="15"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getCapabilityTuples`"
+        errorLine1="            for (RcsContactPresenceTuple t : c.getCapabilityTuples()) {"
+        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="218"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getContactUri`"
+        errorLine1="        b.append(c.getContactUri());"
+        errorLine2="                   ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="211"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getRequestResult`"
+        errorLine1="        b.append(c.getRequestResult());"
+        errorLine2="                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="213"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getSourceType`"
+        errorLine1="        b.append(c.getSourceType());"
+        errorLine2="                   ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="215"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter#requestAvailability`"
+        errorLine1="                mImsRcsManager.getUceAdapter().requestAvailability(contactList.get(0),"
+        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="135"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter#requestCapabilities`"
+        errorLine1="                mImsRcsManager.getUceAdapter().requestCapabilities(contactList, getMainExecutor(),"
+        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="95"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
+        errorLine1='                + ", \n\tmHomeDomain=" + config.getHomeDomain()'
+        errorLine2="                                                ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="341"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
+        errorLine1='                + ", \n\tmImei=" + config.getImei()'
+        errorLine2="                                          ~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="342"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
+        errorLine1='                + ", \n\tmIpSecConfiguration=" + config.getIpSecConfiguration()'
+        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="354"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+        errorLine1='                + ", \n\tmLocalIpAddr=" + config.getLocalAddress()'
+        errorLine2="                                                 ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="334"
+            column="50"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+        errorLine1='                + ", \n\tmMaxUdpPayloadSize=" + config.getMaxUdpPayloadSizeBytes()'
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="338"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getNatSocketAddress`"
+        errorLine1="                + &quot;, \n\tmNatConfiguration=&quot; + config.getNatSocketAddress() + '}';"
+        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="355"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPrivateUserIdentifier`"
+        errorLine1='                + ", \n\tmPrivateUserIdentifier=" + config.getPrivateUserIdentifier()'
+        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="340"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicGruuUri`"
+        errorLine1='                + ", \n\tmGruu=" + config.getPublicGruuUri()'
+        errorLine2="                                          ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="343"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
+        errorLine1='                + ", \n\tmPublicUserIdentifier=" + config.getPublicUserIdentifier()'
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="339"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
+        errorLine1='                + ", \n\tmAssociatedUriHeader=" + config.getSipAssociatedUriHeader()'
+        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="353"
+            column="58"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationHeader`"
+        errorLine1='                + ", \n\tmSipAuthHeader=" + config.getSipAuthenticationHeader()'
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="344"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationNonce`"
+        errorLine1='                + ", \n\tmSipAuthNonce=" + config.getSipAuthenticationNonce()'
+        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="345"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipCniHeader`"
+        errorLine1='                + ", \n\tmCniHeader=" + config.getSipCniHeader()'
+        errorLine2="                                               ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="352"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
+        errorLine1='                + ", \n\tmContactUserParam=" + config.getSipContactUserParameter()'
+        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="349"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
+        errorLine1='                + ", \n\tmPaniHeader=" + config.getSipPaniHeader()'
+        errorLine2="                                                ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="350"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPathHeader`"
+        errorLine1='                + ", \n\tmPathHeader=" + config.getSipPathHeader()'
+        errorLine2="                                                ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="347"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
+        errorLine1='                + ", \n\tmPlaniHeader=" + config.getSipPlaniHeader()'
+        errorLine2="                                                 ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="351"
+            column="50"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+        errorLine1='                + ", \n\tmSipServerAddr=" + config.getSipServerAddress()'
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="335"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
+        errorLine1='                + ", \n\tmServiceRouteHeader=" + config.getSipServiceRouteHeader()'
+        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="346"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
+        errorLine1='                + ", \n\tmUserAgentHeader=" + config.getSipUserAgentHeader()'
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="348"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
+        errorLine1='                + ", \n\tmTransportType=" + config.getTransportType()'
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="333"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+        errorLine1='                + "mVersion=" + config.getVersion()'
+        errorLine2="                                       ~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="332"
+            column="40"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipCompactFormEnabled`"
+        errorLine1='                + ", \n\tmIsSipCompactFormEnabled=" + config.isSipCompactFormEnabled()'
+        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="336"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipKeepaliveEnabled`"
+        errorLine1='                + ", \n\tmIsSipKeepaliveEnabled=" + config.isSipKeepaliveEnabled()'
+        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="337"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#createSipDelegate`"
+        errorLine1="                    mSipDelegateManager.createSipDelegate(new DelegateRequest(featureTags),"
+        errorLine2="                                        ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="231"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
+        errorLine1="                mSipDelegateManager.destroySipDelegate(mSipDelegateConnection,"
+        errorLine2="                                    ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="247"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
+        errorLine1="            mSipDelegateManager.destroySipDelegate(mSipDelegateConnection,"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="322"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.gba.UaSecurityProtocolIdentifier.Builder`"
+        errorLine1="                    new UaSecurityProtocolIdentifier.Builder();"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="120"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.DelegateRequest`"
+        errorLine1="                    mSipDelegateManager.createSipDelegate(new DelegateRequest(featureTags),"
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="231"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
+        errorLine1="        return new RcsClientConfiguration("
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+            line="231"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
+        errorLine1="        return new RcsClientConfiguration("
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="106"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast to `BootstrapAuthenticationCallback` requires API level 31 (current min is 30)"
+        errorLine1="                    new BootstrapAuthenticationCallback() {"
+        errorLine2="                    ^">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="135"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast to `CapabilitiesCallback` requires API level 31 (current min is 30)"
+        errorLine1="                        getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {"
+        errorLine2="                                           ^">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="136"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast to `CapabilitiesCallback` requires API level 31 (current min is 30)"
+        errorLine1="                        new RcsUceAdapter.CapabilitiesCallback() {"
+        errorLine2="                        ^">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="96"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyManager.BootstrapAuthenticationCallback`"
+        errorLine1="                    new BootstrapAuthenticationCallback() {"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="135"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
+        errorLine1="            new RcsProvisioningCallback() {"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+            line="89"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
+        errorLine1="            new RcsProvisioningCallback() {"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="80"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter.CapabilitiesCallback`"
+        errorLine1="                        getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {"
+        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="136"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter.CapabilitiesCallback`"
+        errorLine1="                        new RcsUceAdapter.CapabilitiesCallback() {"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="96"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionMessageCallback`"
+        errorLine1="            new DelegateConnectionMessageCallback() {"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="87"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionStateCallback`"
+        errorLine1="            new DelegateConnectionStateCallback() {"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="117"
+            column="17"/>
+    </issue>
+
+</issues>
\ No newline at end of file
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/lint-baseline.xml b/testapps/TestRcsApp/aosp_test_rcsclient/lint-baseline.xml
new file mode 100644
index 0000000..e0c7c3e
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/lint-baseline.xml
@@ -0,0 +1,928 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.net.ConnectivityManager#registerQosCallback`"
+        errorLine1="            connectivityManager.registerQosCallback(new QosSocketInfo(network, socket),"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
+            line="118"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.net.ConnectivityManager#unregisterQosCallback`"
+        errorLine1="        connectivityManager.unregisterQosCallback(qosCallback);"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
+            line="181"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#bootstrapAuthenticationRequest`"
+        errorLine1="        telephonyManager.bootstrapAuthenticationRequest("
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="97"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#build`"
+        errorLine1="        UaSecurityProtocolIdentifier spId = builder.build();"
+        errorLine2="                                                    ~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="79"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setOrg`"
+        errorLine1="            builder.setOrg(organization)"
+        errorLine2="                    ~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="67"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setProtocol`"
+        errorLine1="                    .setProtocol(protocol);"
+        errorLine2="                     ~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="68"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setTlsCipherSuite`"
+        errorLine1="                builder.setTlsCipherSuite(TlsParams.TLS_RSA_WITH_AES_128_CBC_SHA);"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="70"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#setTlsCipherSuite`"
+        errorLine1="                builder.setTlsCipherSuite(cipherSuite);"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="72"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
+        errorLine1="                                .getRegisteredFeatureTags()"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="139"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
+        errorLine1="            Set&lt;String&gt; registeredFt = registrationState.getRegisteredFeatureTags();"
+        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="223"
+            column="58"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getFeatureTag`"
+        errorLine1='                stringBuilder.append(featureTagState.getFeatureTag()).append(" ").append('
+        errorLine2="                                                     ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="220"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getState`"
+        errorLine1="                        featureTagState.getState());"
+        errorLine2="                                        ~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="221"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ImsManager#getSipDelegateManager`"
+        errorLine1="        this.sipDelegateManager = imsManager.getSipDelegateManager(subscriptionId);"
+        errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="77"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
+        errorLine1="        return provisioningManager.isRcsVolteSingleRegistrationCapable();"
+        errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="166"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
+        errorLine1="            provisioningManager.registerRcsProvisioningCallback(executorService, callback);"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="147"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
+        errorLine1="        provisioningManager.setRcsClientConfiguration(clientConfiguration);"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="111"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#triggerRcsReconfiguration`"
+        errorLine1="        provisioningManager.triggerRcsReconfiguration();"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="176"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+        errorLine1="            provisioningManager.unregisterRcsProvisioningCallback(callback);"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="158"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
+        errorLine1='                    + ", \n\tmHomeDomain=" + config.getHomeDomain()'
+        errorLine2="                                                    ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="246"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
+        errorLine1="            return mConfiguration.getHomeDomain();"
+        errorLine2="                                  ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="317"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
+        errorLine1='                    + ", \n\tmImei=" + config.getImei()'
+        errorLine2="                                              ~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="247"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
+        errorLine1="            return mConfiguration.getImei();"
+        errorLine2="                                  ~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="357"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
+        errorLine1='                    + ", \n\tmIpSecConfiguration=" + config.getIpSecConfiguration()'
+        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="259"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
+        errorLine1="            SipDelegateConfiguration.IpSecConfiguration c = mConfiguration.getIpSecConfiguration();"
+        errorLine2="                                                                           ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="333"
+            column="76"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+        errorLine1='                    + ", \n\tmLocalAddr=" + config.getLocalAddress()'
+        errorLine2="                                                   ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="239"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+        errorLine1="            return mConfiguration.getLocalAddress().getAddress().getHostAddress();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="296"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+        errorLine1="            return mConfiguration.getLocalAddress().getPort();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="301"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+        errorLine1='                    + ", \n\tmMaxUdpPayloadSize=" + config.getMaxUdpPayloadSizeBytes()'
+        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="243"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+        errorLine1="                    ? mConfiguration.getMaxUdpPayloadSizeBytes() : 1500;"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="378"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+        errorLine1="            return mConfiguration.getMaxUdpPayloadSizeBytes() &gt; 0"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="377"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getNatSocketAddress`"
+        errorLine1="                    + &quot;, \n\tmNatConfiguration=&quot; + config.getNatSocketAddress() + '}';"
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="260"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPrivateUserIdentifier`"
+        errorLine1='                    + ", \n\tmPrivateUserIdentifier=" + config.getPrivateUserIdentifier()'
+        errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="245"
+            column="64"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicGruuUri`"
+        errorLine1='                    + ", \n\tmGruu=" + config.getPublicGruuUri()'
+        errorLine2="                                              ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="248"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
+        errorLine1='                    + ", \n\tmPublicUserIdentifier=" + config.getPublicUserIdentifier()'
+        errorLine2="                                                              ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="244"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
+        errorLine1="            return mConfiguration.getPublicUserIdentifier();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="312"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
+        errorLine1='                    + ", \n\tmAssociatedUriHeader=" + config.getSipAssociatedUriHeader()'
+        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="258"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
+        errorLine1="            String associatedUris = mConfiguration.getSipAssociatedUriHeader();"
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="322"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationHeader`"
+        errorLine1='                    + ", \n\tmSipAuthHeader=" + config.getSipAuthenticationHeader()'
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="249"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationNonce`"
+        errorLine1='                    + ", \n\tmSipAuthNonce=" + config.getSipAuthenticationNonce()'
+        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="250"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipCniHeader`"
+        errorLine1='                    + ", \n\tmCniHeader=" + config.getSipCniHeader()'
+        errorLine2="                                                   ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="257"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
+        errorLine1='                    + ", \n\tmContactUserParam=" + config.getSipContactUserParameter()'
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="254"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
+        errorLine1="            return mConfiguration.getSipContactUserParameter();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="352"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
+        errorLine1='                    + ", \n\tmPaniHeader=" + config.getSipPaniHeader()'
+        errorLine2="                                                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="255"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
+        errorLine1="            return mConfiguration.getSipPaniHeader();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="362"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPathHeader`"
+        errorLine1='                    + ", \n\tmPathHeader=" + config.getSipPathHeader()'
+        errorLine2="                                                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="252"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
+        errorLine1='                    + ", \n\tmPlaniHeader=" + config.getSipPlaniHeader()'
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="256"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
+        errorLine1="            return mConfiguration.getSipPlaniHeader();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="367"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+        errorLine1='                    + ", \n\tmSipServerAddr=" + config.getSipServerAddress()'
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="240"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+        errorLine1="            return mConfiguration.getSipServerAddress().getAddress().getHostAddress();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="286"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+        errorLine1="            return mConfiguration.getSipServerAddress().getPort();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="291"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
+        errorLine1='                    + ", \n\tmServiceRouteHeader=" + config.getSipServiceRouteHeader()'
+        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="251"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
+        errorLine1="                    mConfiguration.getSipServiceRouteHeader();"
+        errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="343"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
+        errorLine1='                    + ", \n\tmUserAgentHeader=" + config.getSipUserAgentHeader()'
+        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="253"
+            column="58"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
+        errorLine1="            return mConfiguration.getSipUserAgentHeader();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="372"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
+        errorLine1='                    + ", \n\tmTransportType=" + config.getTransportType()'
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="238"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
+        errorLine1="            int sipTransport = mConfiguration.getTransportType();"
+        errorLine2="                                              ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="306"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+        errorLine1="                                        + registeredSipConfig.getVersion());"
+        errorLine2="                                                              ~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="127"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+        errorLine1='                    + "mVersion=" + config.getVersion()'
+        errorLine2="                                           ~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="237"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+        errorLine1="            return mConfiguration.getVersion();"
+        errorLine2="                                  ~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="281"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipCompactFormEnabled`"
+        errorLine1='                    + ", \n\tmIsSipCompactFormEnabled=" + config.isSipCompactFormEnabled()'
+        errorLine2="                                                                 ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="241"
+            column="66"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipKeepaliveEnabled`"
+        errorLine1='                    + ", \n\tmIsSipKeepaliveEnabled=" + config.isSipKeepaliveEnabled()'
+        errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="242"
+            column="64"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#getSipSecurityVerifyHeader`"
+        errorLine1="            return c.getSipSecurityVerifyHeader();"
+        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="337"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConnection#sendMessage`"
+        errorLine1="            sipDelegateConnection.sendMessage(MessageConverter.toPlatformMessage(message),"
+        errorLine2="                                  ~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="271"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#createSipDelegate`"
+        errorLine1="                controller.sipDelegateManager.createSipDelegate("
+        errorLine2="                                              ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="205"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
+        errorLine1="            sipDelegateManager.destroySipDelegate(context.sipDelegateConnection,"
+        errorLine2="                               ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="92"
+            column="32"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getContent`"
+        errorLine1="            return new SipMessage(message.getStartLine(), headers, message.getContent());"
+        errorLine2="                                                                           ~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="395"
+            column="76"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getHeaderSection`"
+        errorLine1='                        + message.getHeaderSection().substring(0, 10) + "-&gt;"'
+        errorLine2="                                  ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="392"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getHeaderSection`"
+        errorLine1="            String headers = message.getHeaderSection();"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="387"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getStartLine`"
+        errorLine1="            return new SipMessage(message.getStartLine(), headers, message.getContent());"
+        errorLine2="                                          ~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="395"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#toEncodedMessage`"
+        errorLine1="                            message.toEncodedMessage(),"
+        errorLine2="                                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java"
+            line="91"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#toEncodedMessage`"
+        errorLine1="                return (Message) method.invoke(new StringMsgParser(), message.toEncodedMessage());"
+        errorLine2="                                                                              ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java"
+            line="101"
+            column="79"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.net.QosSocketInfo`"
+        errorLine1="            connectivityManager.registerQosCallback(new QosSocketInfo(network, socket),"
+        errorLine2="                                                    ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
+            line="118"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.gba.UaSecurityProtocolIdentifier.Builder`"
+        errorLine1="                new UaSecurityProtocolIdentifier.Builder();"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="55"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.DelegateRequest`"
+        errorLine1="            DelegateRequest request = new DelegateRequest(imsService.getFeatureTags());"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="203"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
+        errorLine1="        return new RcsClientConfiguration("
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="74"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.SipMessage`"
+        errorLine1="            return new SipMessage(message.getStartLine(), headers, message.getContent());"
+        errorLine2="                   ~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="395"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.SipMessage`"
+        errorLine1="        return new SipMessage(startLine, headers.toString(), rawContent);"
+        errorLine2="               ~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java"
+            line="72"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast to `BootstrapAuthenticationCallback` requires API level 31 (current min is 30)"
+        errorLine1="                new TelephonyManager.BootstrapAuthenticationCallback() {"
+        errorLine2="                ^">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="81"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast to `RcsProvisioningCallback` requires API level 31 (current min is 30)"
+        errorLine1="                new RcsProvisioningCallback() {"
+        errorLine2="                ^">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="114"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.net.QosCallback`"
+        errorLine1="    private final QosCallback qosCallback = new QosCallback() {"
+        errorLine2="                                                ~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
+            line="94"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyManager.BootstrapAuthenticationCallback`"
+        errorLine1="                new TelephonyManager.BootstrapAuthenticationCallback() {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="81"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
+        errorLine1="                new RcsProvisioningCallback() {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="114"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionMessageCallback`"
+        errorLine1="                new DelegateConnectionMessageCallback() {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="158"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionStateCallback`"
+        errorLine1="                new DelegateConnectionStateCallback() {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="114"
+            column="21"/>
+    </issue>
+
+</issues>
\ No newline at end of file
diff --git a/tests/src/com/android/phone/PhoneUtilsTest.java b/tests/src/com/android/phone/PhoneUtilsTest.java
index b5ff0dc..3d7815c 100644
--- a/tests/src/com/android/phone/PhoneUtilsTest.java
+++ b/tests/src/com/android/phone/PhoneUtilsTest.java
@@ -82,8 +82,8 @@
     public void testMakePstnPhoneAccountHandleWithPrefix() throws Exception {
         PhoneAccountHandle phoneAccountHandleTest = new PhoneAccountHandle(
                 PSTN_CONNECTION_SERVICE_COMPONENT, mPhoneAccountHandleIdString);
-        assertEquals(phoneAccountHandleTest, PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
-                mPhoneAccountHandleIdString, "", false, null));
+        assertEquals(phoneAccountHandleTest, PhoneUtils.makePstnPhoneAccountHandleWithId(
+                mPhoneAccountHandleIdString, null));
     }
 
     @Test
@@ -91,7 +91,7 @@
         UserHandle userHandle = new UserHandle(10);
         PhoneAccountHandle phoneAccountHandleTest = new PhoneAccountHandle(
                 PSTN_CONNECTION_SERVICE_COMPONENT, mPhoneAccountHandleIdString, userHandle);
-        assertEquals(phoneAccountHandleTest, PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
-                mPhoneAccountHandleIdString, "", false, userHandle));
+        assertEquals(phoneAccountHandleTest, PhoneUtils.makePstnPhoneAccountHandleWithId(
+                mPhoneAccountHandleIdString, userHandle));
     }
 }
diff --git a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
index 921babb..544d7ea 100644
--- a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
+++ b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
@@ -715,7 +715,7 @@
         doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mServiceState).getDataNetworkType();
         // entitlement check passed
         mEntitlementResponse.mEntitlementStatus =
-                PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED;
+                PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED;
 
         // send purchase request
         mSlicePurchaseController.purchasePremiumCapability(
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index fcef25a..e2a199b 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -643,7 +643,7 @@
 
         ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
                 mMockTelephonyConnectionServiceProxy, mConferenceHost,
-                null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+                null /* phoneAccountHandle */, () -> true /* isUsingSinglePartyCallEmulation */,
                 new ImsConference.CarrierConfiguration.Builder()
                         .setShouldLocalDisconnectEmptyConference(true)
                         .build());
@@ -675,6 +675,109 @@
     }
 
     /**
+     * Preconditions: both single party emulation and local disconnect of empty conferences is
+     * enabled.
+     * Tests the case where we receive a repeat with the same single-party data that caused a
+     * conference to be treated as a single party; we need to verify that we do not disconnect the
+     * conference locally in this case.
+     * @throws Exception
+     */
+    @Test
+    @SmallTest
+    public void testNoLocalDisconnectSinglePartyConferenceOnRepeatedCep() throws Exception {
+        when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+                .thenReturn(false);
+
+        ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+                mMockTelephonyConnectionServiceProxy, mConferenceHost,
+                null /* phoneAccountHandle */, () -> true /* isUsingSinglePartyCallEmulation */,
+                new ImsConference.CarrierConfiguration.Builder()
+                        .setShouldLocalDisconnectEmptyConference(true)
+                        .build());
+
+        ConferenceParticipant participant1 = new ConferenceParticipant(
+                Uri.parse("tel:6505551212"),
+                "A",
+                Uri.parse("sip:6505551212@testims.com"),
+                Connection.STATE_ACTIVE,
+                Call.Details.DIRECTION_INCOMING);
+        ConferenceParticipant participant2 = new ConferenceParticipant(
+                Uri.parse("tel:6505551213"),
+                "A",
+                Uri.parse("sip:6505551213@testims.com"),
+                Connection.STATE_ACTIVE,
+                Call.Details.DIRECTION_INCOMING);
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1, participant2));
+        assertEquals(2, imsConference.getNumberOfParticipants());
+
+        // Drop to 1 participant which enters single party mode.
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1));
+        assertEquals(0, imsConference.getNumberOfParticipants());
+
+        // Get a repeat CEP with the same participant data; we should still be in single party mode
+        // but we should NOT disconnect the conference.
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1));
+        assertEquals(0, imsConference.getNumberOfParticipants());
+        verify(mConferenceHost.mMockCall, never()).hangup();
+    }
+
+    /**
+     * An extension of {@link #testNoLocalDisconnectSinglePartyConferenceOnRepeatedCep()} where we
+     * get a repeated CEP with the same single party state, but then finally get a CEP with no
+     * participants anymore.  In this case we do expect a local disconnect as the final state.
+     * @throws Exception
+     */
+    @Test
+    @SmallTest
+    public void testLocalDisconnectSinglePartyConferenceOnRepeatedCep() throws Exception {
+        when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+                .thenReturn(false);
+
+        ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+                mMockTelephonyConnectionServiceProxy, mConferenceHost,
+                null /* phoneAccountHandle */, () -> true /* isUsingSinglePartyCallEmulation */,
+                new ImsConference.CarrierConfiguration.Builder()
+                        .setShouldLocalDisconnectEmptyConference(true)
+                        .build());
+
+        ConferenceParticipant participant1 = new ConferenceParticipant(
+                Uri.parse("tel:6505551212"),
+                "A",
+                Uri.parse("sip:6505551212@testims.com"),
+                Connection.STATE_ACTIVE,
+                Call.Details.DIRECTION_INCOMING);
+        ConferenceParticipant participant2 = new ConferenceParticipant(
+                Uri.parse("tel:6505551213"),
+                "A",
+                Uri.parse("sip:6505551213@testims.com"),
+                Connection.STATE_ACTIVE,
+                Call.Details.DIRECTION_INCOMING);
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1, participant2));
+        assertEquals(2, imsConference.getNumberOfParticipants());
+
+        // Drop to 1 participant which enters single party mode.
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1));
+        assertEquals(0, imsConference.getNumberOfParticipants());
+
+        // Get a repeat CEP with the same participant data; we should still be in single party mode
+        // but we should NOT disconnect the conference.
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1));
+        assertEquals(0, imsConference.getNumberOfParticipants());
+        verify(mConferenceHost.mMockCall, never()).hangup();
+
+        // Got another CEP that has no participants at all; we should disconnet in this case
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost, Collections.emptyList());
+        assertEquals(0, imsConference.getNumberOfParticipants());
+        verify(mConferenceHost.mMockCall).hangup();
+    }
+
+    /**
      * Tests a scenario where a handover connection arrives via
      * {@link TelephonyConnection#onOriginalConnectionRedialed(
      * com.android.internal.telephony.Connection)}.  During this process, the conference properties
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index b156103..f115a43 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -55,7 +55,9 @@
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.telecom.Conference;
+import android.telecom.Conferenceable;
 import android.telecom.ConnectionRequest;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
@@ -91,6 +93,7 @@
 import com.android.internal.telephony.emergency.EmergencyStateTracker;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.satellite.SatelliteSOSMessageRecommender;
 
 import org.junit.After;
 import org.junit.Before;
@@ -122,6 +125,7 @@
     public static class SimpleTelephonyConnection extends TelephonyConnection {
         public boolean wasDisconnected = false;
         public boolean wasUnheld = false;
+        public boolean wasHeld = false;
 
         @Override
         public TelephonyConnection cloneConnection() {
@@ -137,6 +141,11 @@
         public void onUnhold() {
             wasUnheld = true;
         }
+
+        @Override
+        public void onHold() {
+            wasHeld = true;
+        }
     }
 
     public static class SimpleConference extends Conference {
@@ -190,6 +199,8 @@
     @Mock EmergencyCallDomainSelectionConnection mEmergencyCallDomainSelectionConnection;
     @Mock NormalCallDomainSelectionConnection mNormalCallDomainSelectionConnection;
     @Mock ImsPhone mImsPhone;
+    @Mock
+    private SatelliteSOSMessageRecommender mSatelliteSOSMessageRecommender;
     private EmergencyStateTracker mEmergencyStateTracker;
     private Phone mPhone0;
     private Phone mPhone1;
@@ -216,6 +227,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
+        doReturn(Looper.getMainLooper()).when(mContext).getMainLooper();
         mTestConnectionService = new TestTelephonyConnectionService(mContext);
         mTestConnectionService.setPhoneFactoryProxy(mPhoneFactoryProxy);
         mTestConnectionService.setSubscriptionManagerProxy(mSubscriptionManagerProxy);
@@ -241,6 +253,11 @@
         mEmergencyStateTracker = Mockito.mock(EmergencyStateTracker.class);
         replaceInstance(TelephonyConnectionService.class, "mEmergencyStateTracker",
                 mTestConnectionService, mEmergencyStateTracker);
+        replaceInstance(TelephonyConnectionService.class, "mSatelliteSOSMessageRecommender",
+                mTestConnectionService, mSatelliteSOSMessageRecommender);
+        doNothing().when(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), any());
+        doNothing().when(mSatelliteSOSMessageRecommender).onEmergencyCallConnectionStateChanged(
+                anyString(), anyInt());
         doReturn(CompletableFuture.completedFuture(NOT_DISCONNECTED))
                 .when(mEmergencyStateTracker)
                 .startEmergencyCall(any(), anyString(), eq(false));
@@ -1262,6 +1279,7 @@
             // This shouldn't happen
             fail();
         }
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), any());
     }
 
     /**
@@ -1666,6 +1684,24 @@
     }
 
     /**
+     * For DSDA devices, placing an outgoing call on a 2nd sub will hold the existing connection on
+     * the first sub.
+     */
+    @Test
+    @SmallTest
+    public void testHoldOnOtherSubForVirtualDsdaDevice() {
+        when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+
+        ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+        tcs.add(tc1);
+        Conferenceable c = TelephonyConnectionService.maybeHoldCallsOnOtherSubs(
+                tcs, new ArrayList<>(), SUB2_HANDLE, mTelephonyManagerProxy);
+        assertTrue(c.equals(tc1));
+        assertTrue(tc1.wasHeld);
+    }
+
+    /**
      * Verifies that TelephonyManager is used to determine whether a connection is Emergency when
      * creating an outgoing connection.
      */
@@ -1699,6 +1735,7 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
@@ -1728,6 +1765,7 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
@@ -1758,6 +1796,7 @@
 
         verify(mEmergencyStateTracker, times(1))
                 .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
         verify(mDomainSelectionResolver, times(0))
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyCallDomainSelectionConnection, times(0))
@@ -1851,6 +1890,7 @@
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
         verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
@@ -1888,6 +1928,7 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
@@ -1928,6 +1969,7 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
@@ -2191,6 +2233,7 @@
 
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
 
         // dialing is canceled
         mTestConnectionService.onLocalHangup(c);
@@ -2258,6 +2301,7 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), eq(mPhone0));
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
         verify(mPhone0).dial(anyString(), any(), any());
 
@@ -2269,6 +2313,8 @@
 
         TelephonyConnection.TelephonyConnectionListener connectionListener =
                 mTestConnectionService.getEmergencyConnectionListener();
+        TelephonyConnection.TelephonyConnectionListener connectionSatelliteListener =
+                mTestConnectionService.getEmergencyConnectionSatelliteListener();
 
         connectionListener.onOriginalConnectionConfigured(c);
 
@@ -2277,32 +2323,44 @@
 
         verify(mEmergencyStateTracker, times(0)).onEmergencyCallStateChanged(
                 any(), eq(TELECOM_CALL_ID1));
+        verify(mSatelliteSOSMessageRecommender, times(0))
+                .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
 
         c.setActive();
         doReturn(Call.State.ACTIVE).when(orgConn).getState();
         connectionListener.onStateChanged(c, c.getState());
+        connectionSatelliteListener.onStateChanged(c, c.getState());
 
         // ACTIVE sate is notified
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
                 eq(Call.State.ACTIVE), eq(TELECOM_CALL_ID1));
+        verify(mSatelliteSOSMessageRecommender, times(1))
+                .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1),
+                        eq(android.telecom.Connection.STATE_ACTIVE));
 
         // state change to HOLDING
         c.setOnHold();
         doReturn(Call.State.HOLDING).when(orgConn).getState();
         connectionListener.onStateChanged(c, c.getState());
+        connectionSatelliteListener.onStateChanged(c, c.getState());
 
         // state change not notified any more after CONNECTED once
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
                 any(), eq(TELECOM_CALL_ID1));
+        verify(mSatelliteSOSMessageRecommender, times(1))
+                .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
 
         // state change to ACTIVE again
         c.setActive();
         doReturn(Call.State.ACTIVE).when(orgConn).getState();
         connectionListener.onStateChanged(c, c.getState());
+        connectionSatelliteListener.onStateChanged(c, c.getState());
 
         // state change not notified any more after CONNECTED once
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
                 any(), eq(TELECOM_CALL_ID1));
+        verify(mSatelliteSOSMessageRecommender, times(1))
+                .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
 
         // SRVCC happens
         c.setIsImsConnection(false);
@@ -2318,10 +2376,51 @@
         c.setDisconnected(null);
         doReturn(Call.State.DISCONNECTED).when(orgConn).getState();
         connectionListener.onStateChanged(c, c.getState());
+        connectionSatelliteListener.onStateChanged(c, c.getState());
 
         // state change not notified
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
                 any(), eq(TELECOM_CALL_ID1));
+        verify(mSatelliteSOSMessageRecommender, times(1))
+                .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
+    }
+
+    @Test
+    public void testDomainSelectionTempFailure() throws Exception {
+        setupForCallTest();
+
+        int preciseDisconnectCause =
+                com.android.internal.telephony.CallFailCause.EMERGENCY_TEMP_FAILURE;
+        int disconnectCause = android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE;
+        int selectedDomain = DOMAIN_CS;
+
+        TestTelephonyConnection c = setupForReDialForDomainSelection(
+                mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
+
+        doReturn(new CompletableFuture()).when(mEmergencyCallDomainSelectionConnection)
+                .reselectDomain(any());
+
+        assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
+        verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
+    }
+
+    @Test
+    public void testDomainSelectionPermFailure() throws Exception {
+        setupForCallTest();
+
+        int preciseDisconnectCause =
+                com.android.internal.telephony.CallFailCause.EMERGENCY_PERM_FAILURE;
+        int disconnectCause = android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
+        int selectedDomain = DOMAIN_CS;
+
+        TestTelephonyConnection c = setupForReDialForDomainSelection(
+                mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
+
+        doReturn(new CompletableFuture()).when(mEmergencyCallDomainSelectionConnection)
+                .reselectDomain(any());
+
+        assertTrue(mTestConnectionService.maybeReselectDomain(c, preciseDisconnectCause, null));
+        verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
     }
 
     @Test
@@ -2348,6 +2447,7 @@
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
         verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
+        verify(mSatelliteSOSMessageRecommender, never()).onEmergencyCallStarted(any(), any());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
@@ -2372,6 +2472,7 @@
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
         verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
+        verify(mSatelliteSOSMessageRecommender, never()).onEmergencyCallStarted(any(), any());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
index 758ded7..bf9fa01 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
@@ -1,6 +1,9 @@
 package com.android.services.telephony;
 
 import static android.telecom.Connection.STATE_DISCONNECTED;
+import static android.telephony.ims.ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED;
+import static android.telephony.ims.ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL;
+import static android.telephony.ims.ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
@@ -23,6 +26,8 @@
 import android.telecom.Connection;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -39,6 +44,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+
 @RunWith(AndroidJUnit4.class)
 public class TelephonyConnectionTest {
     @Mock
@@ -306,4 +313,58 @@
         assertNotEquals(STATE_DISCONNECTED, c.getState());
         assertTrue(c.isOriginalConnectionCleared());
     }
+
+    @Test
+    public void testDomainSelectionDisconnected_AlternateService() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+        c.setOriginalConnection(mImsPhoneConnection);
+        c.setIsImsConnection(true);
+        doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
+                .getState();
+        doReturn(new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null))
+                .when(mImsPhoneConnection).getImsReasonInfo();
+        doReturn(getEmergencyNumber(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE))
+                .when(mImsPhoneConnection).getEmergencyNumberInfo();
+        c.setTelephonyConnectionService(mTelephonyConnectionService);
+        doReturn(true).when(mTelephonyConnectionService)
+                .maybeReselectDomain(any(), anyInt(), any());
+        c.updateState();
+
+        Integer serviceCategory = c.getEmergencyServiceCategory();
+
+        assertNotNull(serviceCategory);
+        assertEquals(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+                serviceCategory.intValue());
+    }
+
+    @Test
+    public void testDomainSelectionDisconnected_SilentRedialEmergency() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+        c.setOriginalConnection(mImsPhoneConnection);
+        c.setIsImsConnection(true);
+        doReturn(Call.State.DISCONNECTED).when(mImsPhoneConnection)
+                .getState();
+        doReturn(new ImsReasonInfo(CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+                        EXTRA_CODE_CALL_RETRY_EMERGENCY, null))
+                .when(mImsPhoneConnection).getImsReasonInfo();
+        doReturn(getEmergencyNumber(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE))
+                .when(mImsPhoneConnection).getEmergencyNumberInfo();
+        c.setTelephonyConnectionService(mTelephonyConnectionService);
+        doReturn(true).when(mTelephonyConnectionService)
+                .maybeReselectDomain(any(), anyInt(), any());
+        c.updateState();
+
+        Integer serviceCategory = c.getEmergencyServiceCategory();
+
+        assertNotNull(serviceCategory);
+        assertEquals(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+                serviceCategory.intValue());
+    }
+
+    private EmergencyNumber getEmergencyNumber(int eccCategory) {
+        return new EmergencyNumber("", "", "", eccCategory,
+            new ArrayList<String>(),
+            EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+            EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+    }
 }
diff --git a/tests/src/com/android/services/telephony/domainselection/CrossSimRedialingControllerTest.java b/tests/src/com/android/services/telephony/domainselection/CrossSimRedialingControllerTest.java
new file mode 100644
index 0000000..a32329d
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/CrossSimRedialingControllerTest.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2023 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.services.telephony.domainselection;
+
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.REDIAL_TIMER_DISABLED;
+import static android.telephony.PreciseDisconnectCause.EMERGENCY_PERM_FAILURE;
+import static android.telephony.PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE;
+
+import static com.android.services.telephony.domainselection.CrossSimRedialingController.MSG_CROSS_STACK_TIMEOUT;
+import static com.android.services.telephony.domainselection.CrossSimRedialingController.MSG_QUICK_CROSS_STACK_TIMEOUT;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for CrossSimRedialingController
+ */
+public class CrossSimRedialingControllerTest {
+    private static final String TAG = "CrossSimRedialingControllerTest";
+
+    private static final int SLOT_0 = 0;
+    private static final int SLOT_1 = 1;
+
+    private static final String TELECOM_CALL_ID1 = "TC1";
+    private static final String TEST_EMERGENCY_NUMBER = "911";
+
+    @Mock private CarrierConfigManager mCarrierConfigManager;
+    @Mock private TelephonyManager mTelephonyManager;
+    @Mock private EmergencyCallDomainSelector mEcds;
+    @Mock private CrossSimRedialingController.EmergencyNumberHelper mEmergencyNumberHelper;
+
+    private Context mContext;
+
+    private HandlerThread mHandlerThread;
+    private TestableLooper mLooper;
+    private CrossSimRedialingController mCsrController;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = new TestContext() {
+            @Override
+            public String getSystemServiceName(Class<?> serviceClass) {
+                if (serviceClass == TelephonyManager.class) {
+                    return Context.TELEPHONY_SERVICE;
+                } else if (serviceClass == CarrierConfigManager.class) {
+                    return Context.CARRIER_CONFIG_SERVICE;
+                }
+                return super.getSystemServiceName(serviceClass);
+            }
+
+            @Override
+            public String getOpPackageName() {
+                return "";
+            }
+        };
+
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mHandlerThread = new HandlerThread("CrossSimRedialingControllerTest");
+        mHandlerThread.start();
+
+        try {
+            mLooper = new TestableLooper(mHandlerThread.getLooper());
+        } catch (Exception e) {
+            logd("Unable to create looper from handler.");
+        }
+
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        when(mTelephonyManager.createForSubscriptionId(anyInt()))
+                .thenReturn(mTelephonyManager);
+        when(mTelephonyManager.getNetworkCountryIso()).thenReturn("");
+        doReturn(2).when(mTelephonyManager).getActiveModemCount();
+        doReturn(TelephonyManager.SIM_STATE_READY)
+                .when(mTelephonyManager).getSimState(anyInt());
+
+        mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+        doReturn(getDefaultPersistableBundle()).when(mCarrierConfigManager)
+                .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+        doReturn(true).when(mEmergencyNumberHelper).isEmergencyNumber(anyInt(), anyString());
+
+        doReturn(SLOT_0).when(mEcds).getSlotId();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mCsrController != null) {
+            mCsrController.destroy();
+            mCsrController = null;
+        }
+
+        if (mLooper != null) {
+            mLooper.destroy();
+            mLooper = null;
+        }
+    }
+
+    @Test
+    public void testDefaultStartTimerInService() throws Exception {
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        mCsrController.sendEmptyMessage(MSG_CROSS_STACK_TIMEOUT);
+        processAllMessages();
+
+        verify(mEcds).notifyCrossStackTimerExpired();
+    }
+
+    @Test
+    public void testDefaultStartTimerInServiceRoaming() throws Exception {
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = true;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+    }
+
+    @Test
+    public void testDefaultStartTimerOutOfService() throws Exception {
+        createController();
+
+        boolean inService = false;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+    }
+
+    @Test
+    public void testDefaultStartTimerOutOfServiceRoaming() throws Exception {
+        createController();
+
+        boolean inService = false;
+        boolean inRoaming = true;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+    }
+
+    @Test
+    public void testQuickStartTimerInService() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+        doReturn(bundle).when(mCarrierConfigManager)
+                .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        mCsrController.sendEmptyMessage(MSG_QUICK_CROSS_STACK_TIMEOUT);
+        processAllMessages();
+
+        verify(mEcds).notifyCrossStackTimerExpired();
+    }
+
+    @Test
+    public void testQuickStartTimerInServiceRoaming() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+        doReturn(bundle).when(mCarrierConfigManager)
+                .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = true;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+    }
+
+    @Test
+    public void testQuickStartTimerOutOfService() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+        doReturn(bundle).when(mCarrierConfigManager)
+                .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+        createController();
+
+        boolean inService = false;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+    }
+
+    @Test
+    public void testQuickStartTimerOutOfServiceRoaming() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+        doReturn(bundle).when(mCarrierConfigManager)
+                .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+        createController();
+
+        boolean inService = false;
+        boolean inRoaming = true;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+    }
+
+    @Test
+    public void testNoNormalStartTimerInService() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);
+        doReturn(bundle).when(mCarrierConfigManager)
+                .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+    }
+
+    @Test
+    public void testQuickWhenInServiceStartTimerOutOfService() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+        bundle.putBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL, true);
+        doReturn(bundle).when(mCarrierConfigManager)
+                .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+        createController();
+
+        boolean inService = false;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertFalse(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+    }
+
+    @Test
+    public void testQuickNoNormalStartTimerInService() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, 3);
+        bundle.putInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);
+        doReturn(bundle).when(mCarrierConfigManager)
+                .getConfigForSubId(anyInt(), ArgumentMatchers.<String>any());
+
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_QUICK_CROSS_STACK_TIMEOUT));
+        assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+    }
+
+    @Test
+    public void testDefaultSlot0ThenSlot1() throws Exception {
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        mCsrController.removeMessages(MSG_CROSS_STACK_TIMEOUT);
+        assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        doReturn(SLOT_1).when(mEcds).getSlotId();
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+    }
+
+    @Test
+    public void testDefaultSlot0PermThenSlot1Timeout() throws Exception {
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        mCsrController.notifyCallFailure(EMERGENCY_PERM_FAILURE);
+        mCsrController.stopTimer();
+        assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        doReturn(SLOT_1).when(mEcds).getSlotId();
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        mCsrController.sendEmptyMessage(MSG_CROSS_STACK_TIMEOUT);
+        processAllMessages();
+
+        verify(mEcds, times(0)).notifyCrossStackTimerExpired();
+    }
+
+    @Test
+    public void testDefaultSlot0TempThenSlot1Timeout() throws Exception {
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        mCsrController.notifyCallFailure(EMERGENCY_TEMP_FAILURE);
+        mCsrController.stopTimer();
+        assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        doReturn(SLOT_1).when(mEcds).getSlotId();
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        mCsrController.sendEmptyMessage(MSG_CROSS_STACK_TIMEOUT);
+        processAllMessages();
+
+        verify(mEcds).notifyCrossStackTimerExpired();
+    }
+
+    @Test
+    public void testDefaultSlot0TempThenSlot1TimeoutNotEmergencyNumber() throws Exception {
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        mCsrController.notifyCallFailure(EMERGENCY_TEMP_FAILURE);
+        mCsrController.stopTimer();
+        assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        doReturn(SLOT_1).when(mEcds).getSlotId();
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        doReturn(false).when(mEmergencyNumberHelper).isEmergencyNumber(anyInt(), anyString());
+        mCsrController.sendEmptyMessage(MSG_CROSS_STACK_TIMEOUT);
+        processAllMessages();
+
+        verify(mEcds, times(0)).notifyCrossStackTimerExpired();
+    }
+
+    @Test
+    public void testDefaultSlot0TempThenSlot1TimeoutPinLocked() throws Exception {
+        createController();
+
+        boolean inService = true;
+        boolean inRoaming = false;
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        mCsrController.notifyCallFailure(EMERGENCY_TEMP_FAILURE);
+        mCsrController.stopTimer();
+        assertFalse(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        doReturn(SLOT_1).when(mEcds).getSlotId();
+        mCsrController.startTimer(mContext, mEcds, TELECOM_CALL_ID1,
+                TEST_EMERGENCY_NUMBER, inService, inRoaming, 2);
+
+        assertTrue(mCsrController.hasMessages(MSG_CROSS_STACK_TIMEOUT));
+
+        doReturn(TelephonyManager.SIM_STATE_PIN_REQUIRED)
+                .when(mTelephonyManager).getSimState(anyInt());
+        mCsrController.sendEmptyMessage(MSG_CROSS_STACK_TIMEOUT);
+        processAllMessages();
+
+        verify(mEcds, times(0)).notifyCrossStackTimerExpired();
+    }
+
+    private void createController() throws Exception {
+        mCsrController = new CrossSimRedialingController(mContext,
+                mHandlerThread.getLooper(), mEmergencyNumberHelper);
+    }
+
+    private static PersistableBundle getDefaultPersistableBundle() {
+        return getPersistableBundle(0, 120, false);
+    }
+
+    private static PersistableBundle getPersistableBundle(
+            int quickTimer, int timer, boolean startQuickInService) {
+        PersistableBundle bundle  = new PersistableBundle();
+        bundle.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, quickTimer);
+        bundle.putInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT, timer);
+        bundle.putBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL,
+                startQuickInService);
+
+        return bundle;
+    }
+
+    private void processAllMessages() {
+        while (!mLooper.getLooper().getQueue().isIdle()) {
+            mLooper.processAllMessages();
+        }
+    }
+
+    private static void logd(String str) {
+        Log.d(TAG, str);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index 6c7f220..0821943 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -60,6 +60,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -90,6 +91,7 @@
 import android.telephony.DomainSelectionService.SelectionAttributes;
 import android.telephony.EmergencyRegResult;
 import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PreciseDisconnectCause;
 import android.telephony.TelephonyManager;
 import android.telephony.TransportSelectorCallback;
 import android.telephony.WwanSelectorCallback;
@@ -133,6 +135,7 @@
     @Mock private ImsStateTracker mImsStateTracker;
     @Mock private DomainSelectorBase.DestroyListener mDestroyListener;
     @Mock private ProvisioningManager mProvisioningManager;
+    @Mock private CrossSimRedialingController mCsrdCtrl;
 
     private Context mContext;
 
@@ -269,7 +272,7 @@
 
         verify(mWwanSelectorCallback, times(0)).onRequestEmergencyNetworkScan(
                 any(), anyInt(), any(), any());
-        verify(mWwanSelectorCallback, times(0)).onDomainSelected(anyInt());
+        verify(mWwanSelectorCallback, times(0)).onDomainSelected(anyInt(), eq(true));
     }
 
     @Test
@@ -984,13 +987,13 @@
         assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
 
         // Wi-Fi is not connected.
-        verify(mTransportSelectorCallback, times(0)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
 
         // Wi-Fi is connected.
         mNetworkCallback.onAvailable(null);
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
-        verify(mTransportSelectorCallback, times(1)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(true));
     }
 
     @Test
@@ -1016,20 +1019,20 @@
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
         // Wi-Fi is not connected.
-        verify(mTransportSelectorCallback, times(0)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
 
         // Wi-Fi is connected. But Wi-Fi calling setting is disabled.
         mNetworkCallback.onAvailable(null);
         when(mMmTelManager.isVoWiFiRoamingSettingEnabled()).thenReturn(false);
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
-        verify(mTransportSelectorCallback, times(0)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
 
         // Wi-Fi is connected and Wi-Fi calling setting is enabled.
         when(mMmTelManager.isVoWiFiSettingEnabled()).thenReturn(true);
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
-        verify(mTransportSelectorCallback, times(1)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(true));
     }
 
     @Test
@@ -1055,19 +1058,19 @@
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
         // Wi-Fi is not connected.
-        verify(mTransportSelectorCallback, times(0)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
 
         // Wi-Fi is connected. But Wi-Fi calling s not activated.
         mNetworkCallback.onAvailable(null);
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
-        verify(mTransportSelectorCallback, times(0)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
 
         // Wi-Fi is connected and Wi-Fi calling is activated.
         doReturn("1").when(mProvisioningManager).getProvisioningStringValue(anyInt());
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
-        verify(mTransportSelectorCallback, times(1)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(true));
     }
 
     @Test
@@ -1088,19 +1091,19 @@
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
         // Wi-Fi is not connected.
-        verify(mTransportSelectorCallback, times(0)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
 
         // Wi-Fi is connected but IMS is not registered over Wi-Fi.
         mNetworkCallback.onAvailable(null);
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
-        verify(mTransportSelectorCallback, times(0)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
 
         // IMS is registered over Wi-Fi.
         bindImsService(true);
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
-        verify(mTransportSelectorCallback, times(1)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(false));
     }
 
     @Test
@@ -1127,13 +1130,13 @@
 
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
-        verify(mTransportSelectorCallback, times(1)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(false));
 
         // duplicated event
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
         // ignore duplicated callback, no change in interaction
-        verify(mTransportSelectorCallback, times(1)).onWlanSelected();
+        verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
     }
 
     @Test
@@ -1143,6 +1146,7 @@
         doReturn(2).when(mTelephonyManager).getActiveModemCount();
         doReturn(TelephonyManager.SIM_STATE_PIN_REQUIRED)
                 .when(mTelephonyManager).getSimState(anyInt());
+        doReturn(true).when(mCsrdCtrl).isThereOtherSlot();
 
         EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
                 0, false, false, 0, 0, "", "", "jp");
@@ -1158,6 +1162,29 @@
     }
 
     @Test
+    public void testDualSimInvalidSubscriptionButNoOtherSlot() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+        doReturn(2).when(mTelephonyManager).getActiveModemCount();
+        doReturn(TelephonyManager.SIM_STATE_PIN_REQUIRED)
+                .when(mTelephonyManager).getSimState(anyInt());
+        doReturn(false).when(mCsrdCtrl).isThereOtherSlot();
+
+        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "", "jp");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, times(0))
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_PERM_FAILURE));
+        verifyScanPsPreferred();
+    }
+
+    @Test
     public void testEutranWithCsDomainOnly() throws Exception {
         setupForHandleScanResult();
 
@@ -1478,6 +1505,149 @@
         assertTrue(networks.indexOf(GERAN) < networks.indexOf(NGRAN));
     }
 
+    @Test
+    public void testStartCrossStackTimer() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(
+                UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        processAllMessages();
+        verify(mCsrdCtrl).startTimer(any(), eq(mDomainSelector), any(),
+                any(), anyBoolean(), anyBoolean(), anyInt());
+    }
+
+    @Test
+    public void testStopCrossStackTimerOnCancel() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        mDomainSelector.cancelSelection();
+
+        verify(mCsrdCtrl).stopTimer();
+    }
+
+    @Test
+    public void testStopCrossStackTimerOnFinish() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        mDomainSelector.finishSelection();
+
+        verify(mCsrdCtrl).stopTimer();
+    }
+
+    @Test
+    public void testCrossStackTimerTempFailure() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyCsDialed();
+
+        attr = new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setEmergency(true)
+                .setEmergencyRegResult(regResult)
+                .setCsDisconnectCause(PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE)
+                .build();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mCsrdCtrl).notifyCallFailure(eq(PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE));
+    }
+
+    @Test
+    public void testCrossStackTimerPermFailure() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyCsDialed();
+
+        attr = new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setEmergency(true)
+                .setEmergencyRegResult(regResult)
+                .setCsDisconnectCause(PreciseDisconnectCause.EMERGENCY_PERM_FAILURE)
+                .build();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mCsrdCtrl).notifyCallFailure(eq(PreciseDisconnectCause.EMERGENCY_PERM_FAILURE));
+    }
+
+    @Test
+    public void testCrossStackTimerExpired() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(
+                UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyScanPsPreferred();
+
+        mDomainSelector.notifyCrossStackTimerExpired();
+
+        verify(mTransportSelectorCallback)
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+    }
+
+    @Test
+    public void testCrossStackTimerExpiredAfterDomainSelected() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyCsDialed();
+
+        mDomainSelector.notifyCrossStackTimerExpired();
+
+        verify(mTransportSelectorCallback, times(0))
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mTransportSelectorCallback)
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+    }
+
     private void setupForScanListTest(PersistableBundle bundle) throws Exception {
         setupForScanListTest(bundle, false);
     }
@@ -1551,7 +1721,7 @@
     private void createSelector(int subId) throws Exception {
         mDomainSelector = new EmergencyCallDomainSelector(
                 mContext, SLOT_0, subId, mHandlerThread.getLooper(),
-                mImsStateTracker, mDestroyListener);
+                mImsStateTracker, mDestroyListener, mCsrdCtrl);
 
         replaceInstance(DomainSelectorBase.class,
                 "mWwanSelectorCallback", mDomainSelector, mWwanSelectorCallback);
@@ -1559,12 +1729,12 @@
 
     private void verifyCsDialed() {
         processAllMessages();
-        verify(mWwanSelectorCallback, times(1)).onDomainSelected(eq(DOMAIN_CS));
+        verify(mWwanSelectorCallback, times(1)).onDomainSelected(eq(DOMAIN_CS), eq(false));
     }
 
     private void verifyPsDialed() {
         processAllMessages();
-        verify(mWwanSelectorCallback, times(1)).onDomainSelected(eq(DOMAIN_PS));
+        verify(mWwanSelectorCallback, times(1)).onDomainSelected(eq(DOMAIN_PS), eq(true));
     }
 
     private void verifyScanPsPreferred() {
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java
index 8b63530..1d89071 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java
@@ -210,7 +210,8 @@
 
         processAllMessages();
 
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -413,7 +414,8 @@
 
         // onDomainSelected will be invoked only once
         // even though the domain selection was requested twice.
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -430,7 +432,8 @@
         processAllMessages();
 
         // Expected: CS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
     }
 
     @Test
@@ -446,7 +449,8 @@
         processAllMessages();
 
         // Expected: CS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
     }
 
     @Test
@@ -462,7 +466,8 @@
         processAllMessages();
 
         // Expected: CS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
     }
 
     @Test
@@ -478,7 +483,8 @@
         processAllMessages();
 
         // Expected: PS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
     }
 
     @Test
@@ -494,7 +500,8 @@
         processAllMessages();
 
         // Expected: CS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
     }
 
     @Test
@@ -510,7 +517,8 @@
         processAllMessages();
 
         // Expected: CS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
     }
 
     @Test
@@ -526,7 +534,8 @@
         processAllMessages();
 
         // Expected: PS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
     }
 
     @Test
@@ -542,7 +551,8 @@
         processAllMessages();
 
         // Expected: CS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
     }
 
     @Test
@@ -558,7 +568,8 @@
         processAllMessages();
 
         // Expected: CS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
     }
 
     @Test
@@ -574,7 +585,8 @@
         processAllMessages();
 
         // Expected: PS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
     }
 
     @Test
@@ -590,7 +602,8 @@
         processAllMessages();
 
         // Expected: CS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
     }
 
     @Test
@@ -606,7 +619,8 @@
         processAllMessages();
 
         // Expected: CS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
     }
 
     @Test
@@ -622,7 +636,7 @@
         processAllMessages();
 
         // Expected: WLAN
-        verify(mTransportSelectorCallback).onWlanSelected();
+        verify(mTransportSelectorCallback).onWlanSelected(eq(false));
     }
 
     @Test
@@ -641,7 +655,8 @@
         // if the emergency SMS messages over IMS is enabled in the carrier configuration and
         // the PS network does not allow the emergency service, this MO SMS should be routed to
         // CS domain.
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
     }
 
     @Test
@@ -657,7 +672,8 @@
         processAllMessages();
 
         // Expected: PS network
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
     }
 
     private void setUpCarrierConfig(boolean supported) {
diff --git a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
index 8780f1f..4dd1f3c 100644
--- a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
@@ -436,7 +436,7 @@
         }
 
         @Override
-        public synchronized void onWlanSelected() {
+        public synchronized void onWlanSelected(boolean useEmergencyPdn) {
             Log.d(TAG, "onWlanSelected");
             mWlanSelected = true;
             notifyAll();
@@ -504,7 +504,8 @@
 
         }
 
-        public synchronized void onDomainSelected(@NetworkRegistrationInfo.Domain int domain) {
+        public synchronized void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
+                boolean useEmergencyPdn) {
             Log.i(TAG, "onDomainSelected - called");
             mSelectedDomain = domain;
             mDomainSelected = true;
diff --git a/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java
index 412d83d..785127c 100644
--- a/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java
@@ -142,7 +142,8 @@
 
         processAllMessages();
 
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -184,7 +185,8 @@
 
         // onDomainSelected will be invoked only once
         // even though the domain selection was requested twice.
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -230,7 +232,8 @@
 
         processAllMessages();
 
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
 
         mDomainSelector.reselectDomain(mSelectionAttributes);
@@ -239,7 +242,7 @@
 
         processAllMessages();
 
-        verify(mTransportSelectorCallback).onWlanSelected();
+        verify(mTransportSelectorCallback).onWlanSelected(eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -256,9 +259,10 @@
         mDomainSelector.reselectDomain(mSelectionAttributes);
         processAllMessages();
 
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
-        verify(mTransportSelectorCallback, never()).onWlanSelected();
+        verify(mTransportSelectorCallback, never()).onWlanSelected(eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -275,7 +279,8 @@
 
         processAllMessages();
 
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -292,7 +297,8 @@
 
         processAllMessages();
 
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -309,7 +315,8 @@
 
         processAllMessages();
 
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -326,7 +333,8 @@
 
         processAllMessages();
 
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -343,7 +351,8 @@
 
         processAllMessages();
 
-        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
 
@@ -358,9 +367,10 @@
         processAllMessages();
 
         if (accessNetworkType == AccessNetworkType.IWLAN) {
-            verify(mTransportSelectorCallback).onWlanSelected();
+            verify(mTransportSelectorCallback).onWlanSelected(eq(false));
         } else {
-            verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+            verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                    eq(false));
         }
         assertFalse(mDomainSelector.isDomainSelectionRequested());
     }
diff --git a/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
index ace59e3..f340e94 100644
--- a/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
@@ -77,7 +77,8 @@
                 public DomainSelectorBase create(Context context, int slotId, int subId,
                         @SelectorType int selectorType, boolean isEmergency,
                         @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
-                        @NonNull DomainSelectorBase.DestroyListener listener) {
+                        @NonNull DomainSelectorBase.DestroyListener listener,
+                        @NonNull CrossSimRedialingController crossSimRedialingController) {
                     switch (selectorType) {
                         case DomainSelectionService.SELECTOR_TYPE_CALLING: // fallthrough
                         case DomainSelectionService.SELECTOR_TYPE_SMS: // fallthrough