Merge "Delay data switch untill call alerting" into udc-qpr-dev am: c82807bb21 am: 307100c495

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/opt/telephony/+/24747010

Change-Id: If36507d9f550cb03e627e964b46a6bd9d0826572
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index 892a48a..66cca32 100644
--- a/Android.bp
+++ b/Android.bp
@@ -83,14 +83,15 @@
         "android.hardware.radio-V1.4-java",
         "android.hardware.radio-V1.5-java",
         "android.hardware.radio-V1.6-java",
-        "android.hardware.radio.config-V2-java",
-        "android.hardware.radio.data-V2-java",
-        "android.hardware.radio.ims-V1-java",
-        "android.hardware.radio.messaging-V2-java",
-        "android.hardware.radio.modem-V2-java",
-        "android.hardware.radio.network-V2-java",
-        "android.hardware.radio.sim-V2-java",
-        "android.hardware.radio.voice-V2-java",
+        "android.hardware.radio.config-V3-java",
+        "android.hardware.radio.data-V3-java",
+        "android.hardware.radio.ims-V2-java",
+        "android.hardware.radio.messaging-V3-java",
+        "android.hardware.radio.modem-V3-java",
+        "android.hardware.radio.network-V3-java",
+        "android.hardware.radio.sim-V3-java",
+        "android.hardware.radio.satellite-V1-java",
+        "android.hardware.radio.voice-V3-java",
         "voip-common",
         "ims-common",
         "unsupportedappusage",
@@ -100,7 +101,6 @@
         "android.hardware.radio.config-V1.1-java-shallow",
         "android.hardware.radio.config-V1.2-java-shallow",
         "android.hardware.radio.config-V1.3-java-shallow",
-        "android.hardware.radio.deprecated-V1.0-java-shallow",
         "ecc-protos-lite",
         "libphonenumber-nogeocoder",
         "PlatformProperties",
diff --git a/flags/Android.bp b/flags/Android.bp
new file mode 100644
index 0000000..7486677
--- /dev/null
+++ b/flags/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aconfig_declarations {
+    name: "telephony_flags",
+    package: "com.android.internal.telephony.flags",
+    srcs: [
+      "data.aconfig",
+      "ims.aconfig",
+      "messaging.aconfig",
+      "misc.aconfig",
+      "subscription.aconfig",
+      "uicc.aconfig",
+      "satellite.aconfig",
+    ],
+}
diff --git a/flags/data.aconfig b/flags/data.aconfig
new file mode 100644
index 0000000..9a5b506
--- /dev/null
+++ b/flags/data.aconfig
@@ -0,0 +1 @@
+package: "com.android.internal.telephony.flags"
diff --git a/flags/ims.aconfig b/flags/ims.aconfig
new file mode 100644
index 0000000..84e491e
--- /dev/null
+++ b/flags/ims.aconfig
@@ -0,0 +1 @@
+package: "com.android.internal.telephony.flags"
\ No newline at end of file
diff --git a/flags/messaging.aconfig b/flags/messaging.aconfig
new file mode 100644
index 0000000..84e491e
--- /dev/null
+++ b/flags/messaging.aconfig
@@ -0,0 +1 @@
+package: "com.android.internal.telephony.flags"
\ No newline at end of file
diff --git a/flags/misc.aconfig b/flags/misc.aconfig
new file mode 100644
index 0000000..84e491e
--- /dev/null
+++ b/flags/misc.aconfig
@@ -0,0 +1 @@
+package: "com.android.internal.telephony.flags"
\ No newline at end of file
diff --git a/flags/satellite.aconfig b/flags/satellite.aconfig
new file mode 100644
index 0000000..e640e6e
--- /dev/null
+++ b/flags/satellite.aconfig
@@ -0,0 +1,15 @@
+package: "com.android.internal.telephony.flags"
+
+flag {
+    name: "oem_enabled_satellite_flag"
+    namespace: "telephony"
+    description: "This flag controls satellite communication supported by OEMs."
+    bug:"291811962"
+}
+
+flag {
+    name: "carrier_enabled_satellite_flag"
+    namespace: "telephony"
+    description: "This flag controls satellite communication supported by carriers."
+    bug:"296437388"
+}
\ No newline at end of file
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig
new file mode 100644
index 0000000..0521cb9
--- /dev/null
+++ b/flags/subscription.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.internal.telephony.flags"
+
+flag {
+  name: "work_profile_api_split"
+  namespace: "telephony"
+  description: "To support separation between personal and work from TelephonyManager and SubscriptionManager API perspective."
+  bug: "296076674"
+}
\ No newline at end of file
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig
new file mode 100644
index 0000000..84e491e
--- /dev/null
+++ b/flags/uicc.aconfig
@@ -0,0 +1 @@
+package: "com.android.internal.telephony.flags"
\ No newline at end of file
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 9caa0f7..5205616 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -190,6 +190,12 @@
     /* Number of time the user toggled the data switch feature since the last collection. */
     optional int32 auto_data_switch_toggle_count = 55;
 
+    /* Consolidated emergency numbers list information. */
+    repeated EmergencyNumbersInfo emergency_numbers_info = 56;
+
+    /* Timestamp of last emergency number pull. */
+    optional int64 emergency_number_pull_timestamp_millis = 57;
+
     /** Snapshot of satellite controller. */
     repeated SatelliteController satellite_controller = 58;
 
@@ -225,12 +231,6 @@
 
     /* Timestamp of last satellite_sos_message_recommender pull. */
     optional int64 satellite_sos_message_recommender_pull_timestamp_millis = 69;
-
-    /* Consolidated emergency numbers list information. */
-    repeated EmergencyNumbersInfo emergency_numbers_info = 56;
-
-    /* Timestamp of last emergency number pull. */
-    optional int64 emergency_number_pull_timestamp_millis = 57;
 }
 
 // The canonical versions of the following enums live in:
@@ -587,6 +587,41 @@
     optional int32 short_code_sms_count = 3;
 }
 
+message EmergencyNumbersInfo {
+    enum ServiceCategory {
+        EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = 0;
+        EMERGENCY_SERVICE_CATEGORY_POLICE = 1;
+        EMERGENCY_SERVICE_CATEGORY_AMBULANCE = 2;
+        EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = 3;
+        EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = 4;
+        EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = 5;
+        EMERGENCY_SERVICE_CATEGORY_MIEC = 6;
+        EMERGENCY_SERVICE_CATEGORY_AIEC = 7;
+    }
+    enum Source {
+        EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 0;
+        EMERGENCY_NUMBER_SOURCE_SIM = 1;
+        EMERGENCY_NUMBER_SOURCE_DATABASE = 2;
+        EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 3;
+        EMERGENCY_NUMBER_SOURCE_DEFAULT = 4;
+    }
+    enum CallRoute {
+        EMERGENCY_CALL_ROUTE_UNKNOWN = 0;
+        EMERGENCY_CALL_ROUTE_EMERGENCY = 1;
+        EMERGENCY_CALL_ROUTE_NORMAL = 2;
+    }
+    optional bool is_db_version_ignored = 1;
+    optional int32 asset_version = 2;
+    optional int32 ota_version = 3;
+    optional string number = 4;
+    optional string country_iso = 5;
+    optional string mnc = 6;
+    optional CallRoute route = 7;
+    repeated string urns = 8;
+    repeated ServiceCategory service_categories = 9;
+    repeated Source sources = 10;
+}
+
 message SatelliteController {
     optional int32 count_of_satellite_service_enablements_success = 1;
     optional int32 count_of_satellite_service_enablements_fail = 2;
@@ -640,38 +675,3 @@
     optional int32 cellular_service_state = 4;
     optional int32 count = 5;
 }
-
-message EmergencyNumbersInfo {
-    enum ServiceCategory {
-        EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = 0;
-        EMERGENCY_SERVICE_CATEGORY_POLICE = 1;
-        EMERGENCY_SERVICE_CATEGORY_AMBULANCE = 2;
-        EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = 3;
-        EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = 4;
-        EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = 5;
-        EMERGENCY_SERVICE_CATEGORY_MIEC = 6;
-        EMERGENCY_SERVICE_CATEGORY_AIEC = 7;
-    }
-    enum Source {
-        EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 0;
-        EMERGENCY_NUMBER_SOURCE_SIM = 1;
-        EMERGENCY_NUMBER_SOURCE_DATABASE = 2;
-        EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 3;
-        EMERGENCY_NUMBER_SOURCE_DEFAULT = 4;
-    }
-    enum CallRoute {
-        EMERGENCY_CALL_ROUTE_UNKNOWN = 0;
-        EMERGENCY_CALL_ROUTE_EMERGENCY = 1;
-        EMERGENCY_CALL_ROUTE_NORMAL = 2;
-    }
-    optional bool is_db_version_ignored = 1;
-    optional int32 asset_version = 2;
-    optional int32 ota_version = 3;
-    optional string number = 4;
-    optional string country_iso = 5;
-    optional string mnc = 6;
-    optional CallRoute route = 7;
-    repeated string urns = 8;
-    repeated ServiceCategory service_categories = 9;
-    repeated Source sources = 10;
-}
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index b8de975..f461e68 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -69,8 +69,6 @@
     protected RegistrantList mVoicePrivacyOnRegistrants = new RegistrantList();
     protected RegistrantList mVoicePrivacyOffRegistrants = new RegistrantList();
     @UnsupportedAppUsage
-    protected Registrant mUnsolOemHookRawRegistrant;
-    @UnsupportedAppUsage
     protected RegistrantList mOtaProvisionRegistrants = new RegistrantList();
     @UnsupportedAppUsage
     protected RegistrantList mCallWaitingInfoRegistrants = new RegistrantList();
@@ -681,17 +679,6 @@
         mSignalInfoRegistrants.addUnique(h, what, obj);
     }
 
-    public void setOnUnsolOemHookRaw(Handler h, int what, Object obj) {
-        mUnsolOemHookRawRegistrant = new Registrant (h, what, obj);
-    }
-
-    public void unSetOnUnsolOemHookRaw(Handler h) {
-        if (mUnsolOemHookRawRegistrant != null && mUnsolOemHookRawRegistrant.getHandler() == h) {
-            mUnsolOemHookRawRegistrant.clear();
-            mUnsolOemHookRawRegistrant = null;
-        }
-    }
-
     @Override
     public void unregisterForSignalInfo(Handler h) {
         mSignalInfoRegistrants.remove(h);
@@ -1011,18 +998,6 @@
     }
 
     @Override
-    public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
-    }
-
-    @Override
-    public void stopLceService(Message result) {
-    }
-
-    @Override
-    public void pullLceData(Message result) {
-    }
-
-    @Override
     public void registerForLceInfo(Handler h, int what, Object obj) {
         synchronized (mStateMonitor) {
             mLceInfoRegistrants.addUnique(h, what, obj);
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index 93f5ab0..8c905c3 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -19,13 +19,17 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.telephony.RadioAccessFamily;
@@ -66,6 +70,11 @@
     public static final int NOTIFICATION_PREF_NETWORK = 1000;
     public static final int NOTIFICATION_EMERGENCY_NETWORK = 1001;
 
+
+    @VisibleForTesting
+    public static final String ACTION_NEVER_ASK_AGAIN = "SilenceNoWifiEmrgCallingNotification";
+    public final NotificationActionReceiver mActionReceiver = new NotificationActionReceiver();
+
     @VisibleForTesting
     public static final String EMERGENCY_NOTIFICATION_TAG = "EmergencyNetworkNotification";
 
@@ -150,6 +159,12 @@
                         TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
         mAllowedNetworkTypesListener = new AllowedNetworkTypesListener();
         registerAllowedNetworkTypesListener();
+
+        // register a receiver for notification actions
+        mPhone.getContext().registerReceiver(
+                mActionReceiver,
+                new IntentFilter(ACTION_NEVER_ASK_AGAIN),
+                Context.RECEIVER_NOT_EXPORTED);
     }
 
     /**
@@ -376,11 +391,17 @@
      */
     @VisibleForTesting
     public void sendNotification(NotificationType notificationType) {
+        Context context = mPhone.getContext();
+
         if (!evaluateSendingMessage(notificationType)) {
             return;
         }
 
-        Context context = mPhone.getContext();
+        if (shouldSilenceEmrgNetNotif(notificationType, context)) {
+            Rlog.i(LOG_TAG, "sendNotification: silencing NOTIFICATION_EMERGENCY_NETWORK");
+            return;
+        }
+
         Notification.Builder builder = getNotificationBuilder(notificationType);
         // set some common attributes
         builder.setWhen(System.currentTimeMillis())
@@ -394,6 +415,15 @@
     }
 
     /**
+     * This helper checks if the user has set a flag to silence the notification permanently
+     */
+    private boolean shouldSilenceEmrgNetNotif(NotificationType notificationType, Context context) {
+        return notificationType.getTypeId() == NOTIFICATION_EMERGENCY_NETWORK
+                && PreferenceManager.getDefaultSharedPreferences(context)
+                .getBoolean(ACTION_NEVER_ASK_AGAIN, false);
+    }
+
+    /**
      * Cancel notifications if a registration is pending or has been sent.
      **/
     public void cancelNotification(NotificationType notificationType) {
@@ -651,7 +681,43 @@
                     .setStyle(new Notification.BigTextStyle().bigText(details))
                     .setContentText(details)
                     .setOngoing(true)
+                    .setActions(createDoNotShowAgainAction(context))
                     .setChannelId(NotificationChannelController.CHANNEL_ID_WFC);
         }
+
+        /**
+         * add a button to the notification that has a broadcast intent embedded to silence the
+         * notification
+         */
+        private Notification.Action createDoNotShowAgainAction(Context context) {
+            final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                    context,
+                    0,
+                    new Intent(ACTION_NEVER_ASK_AGAIN),
+                    PendingIntent.FLAG_IMMUTABLE);
+            return new Notification.Action.Builder(null, "Do Not Show Again",
+                    pendingIntent).build();
+        }
+    }
+
+    /**
+     * This receiver listens to notification actions and can be utilized to do things like silence
+     * a notification that is spammy.
+     */
+    public class NotificationActionReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(ACTION_NEVER_ASK_AGAIN)) {
+                Rlog.i(LOG_TAG, "NotificationActionReceiver: ACTION_NEVER_ASK_AGAIN");
+                // insert a key to silence future notifications
+                SharedPreferences.Editor editor =
+                        PreferenceManager.getDefaultSharedPreferences(context).edit();
+                editor.putBoolean(ACTION_NEVER_ASK_AGAIN, true);
+                editor.apply();
+                // Note: If another action is added, unregistering here should be removed. However,
+                // since there is no longer a reason to broadcasts, cleanup mActionReceiver.
+                context.unregisterReceiver(mActionReceiver);
+            }
+        }
     }
 }
diff --git a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
index 33cde4f..e187989 100644
--- a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
+++ b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
@@ -58,7 +58,7 @@
     public static final int EVENT_ON_FILTER_COMPLETE_NOT_CALLED = 1;
 
     /** onFilterComplete timeout. */
-    public static final int FILTER_COMPLETE_TIMEOUT_MS = 10 * 60 * 1000; //10 minutes
+    public static final int FILTER_COMPLETE_TIMEOUT_MS = 12 * 60 * 1000; //12 minutes
 
     /** SMS anomaly uuid -- CarrierMessagingService did not respond */
     private static final UUID sAnomalyNoResponseFromCarrierMessagingService =
@@ -381,7 +381,9 @@
         }
 
         private void addToCallbacks(CarrierSmsFilterCallback callback) {
-            mCallbacks.add(callback);
+            synchronized (mFilterLock) {
+                mCallbacks.add(callback);
+            }
         }
 
     }
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
index 9cbd7a6..bff1d41 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkService.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -25,9 +25,11 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.AnomalyReporter;
+import android.telephony.CarrierConfigManager;
 import android.telephony.CellIdentity;
 import android.telephony.CellIdentityCdma;
 import android.telephony.CellIdentityGsm;
@@ -42,6 +44,7 @@
 import android.telephony.NetworkServiceCallback;
 import android.telephony.NrVopsSupportInfo;
 import android.telephony.ServiceState;
+import android.telephony.SmsManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.VopsSupportInfo;
@@ -230,6 +233,10 @@
                     || regState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
                 if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
                     availableServices.add(NetworkRegistrationInfo.SERVICE_TYPE_DATA);
+                    if (isMmsEnabled(mPhone)) {
+                        // Add SERVICE_TYPE_MMS only if MMS is enabled
+                        availableServices.add(NetworkRegistrationInfo.SERVICE_TYPE_MMS);
+                    }
                 } else if (domain == NetworkRegistrationInfo.DOMAIN_CS) {
                     availableServices.add(NetworkRegistrationInfo.SERVICE_TYPE_VOICE);
                     availableServices.add(NetworkRegistrationInfo.SERVICE_TYPE_SMS);
@@ -279,27 +286,6 @@
             } else if (result instanceof android.hardware.radio.V1_5.RegStateResult) {
                 return getNetworkRegistrationInfo(domain, transportType,
                         (android.hardware.radio.V1_5.RegStateResult) result);
-            } else if (result instanceof android.hardware.radio.V1_0.VoiceRegStateResult) {
-                android.hardware.radio.V1_0.VoiceRegStateResult voiceRegState =
-                        (android.hardware.radio.V1_0.VoiceRegStateResult) result;
-                int regState = getRegStateFromHalRegState(voiceRegState.regState);
-                int networkType = ServiceState.rilRadioTechnologyToNetworkType(voiceRegState.rat);
-                int reasonForDenial = voiceRegState.reasonForDenial;
-                boolean emergencyOnly = isEmergencyOnly(voiceRegState.regState);
-                boolean cssSupported = voiceRegState.cssSupported;
-                int roamingIndicator = voiceRegState.roamingIndicator;
-                int systemIsInPrl = voiceRegState.systemIsInPrl;
-                int defaultRoamingIndicator = voiceRegState.defaultRoamingIndicator;
-                List<Integer> availableServices = getAvailableServices(
-                        regState, domain, emergencyOnly);
-                CellIdentity cellIdentity =
-                        RILUtils.convertHalCellIdentity(voiceRegState.cellIdentity);
-                final String rplmn = getPlmnFromCellIdentity(cellIdentity);
-
-                return new NetworkRegistrationInfo(domain, transportType, regState,
-                        networkType, reasonForDenial, emergencyOnly, availableServices,
-                        cellIdentity, rplmn, cssSupported, roamingIndicator, systemIsInPrl,
-                        defaultRoamingIndicator);
             } else if (result instanceof android.hardware.radio.V1_2.VoiceRegStateResult) {
                 android.hardware.radio.V1_2.VoiceRegStateResult voiceRegState =
                         (android.hardware.radio.V1_2.VoiceRegStateResult) result;
@@ -330,20 +316,6 @@
             final int domain = NetworkRegistrationInfo.DOMAIN_PS;
             final int transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
 
-            int regState = NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
-            int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-            int reasonForDenial = 0;
-            boolean emergencyOnly = false;
-            int maxDataCalls = 0;
-            CellIdentity cellIdentity;
-            boolean isEndcAvailable = false;
-            boolean isNrAvailable = false;
-            boolean isDcNrRestricted = false;
-
-            LteVopsSupportInfo lteVopsSupportInfo =
-                    new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
-                            LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
-
             if (result instanceof android.hardware.radio.network.RegStateResult) {
                 return getNetworkRegistrationInfoAidl(domain, transportType,
                         (android.hardware.radio.network.RegStateResult) result);
@@ -353,66 +325,46 @@
             } else if (result instanceof android.hardware.radio.V1_5.RegStateResult) {
                 return getNetworkRegistrationInfo(domain, transportType,
                         (android.hardware.radio.V1_5.RegStateResult) result);
-            } else if (result instanceof android.hardware.radio.V1_0.DataRegStateResult) {
-                android.hardware.radio.V1_0.DataRegStateResult dataRegState =
-                        (android.hardware.radio.V1_0.DataRegStateResult) result;
-                regState = getRegStateFromHalRegState(dataRegState.regState);
-                networkType = ServiceState.rilRadioTechnologyToNetworkType(dataRegState.rat);
-                reasonForDenial = dataRegState.reasonDataDenied;
-                emergencyOnly = isEmergencyOnly(dataRegState.regState);
-                maxDataCalls = dataRegState.maxDataCalls;
-                cellIdentity = RILUtils.convertHalCellIdentity(dataRegState.cellIdentity);
-            } else if (result instanceof android.hardware.radio.V1_2.DataRegStateResult) {
-                android.hardware.radio.V1_2.DataRegStateResult dataRegState =
-                        (android.hardware.radio.V1_2.DataRegStateResult) result;
-                regState = getRegStateFromHalRegState(dataRegState.regState);
-                networkType = ServiceState.rilRadioTechnologyToNetworkType(dataRegState.rat);
-                reasonForDenial = dataRegState.reasonDataDenied;
-                emergencyOnly = isEmergencyOnly(dataRegState.regState);
-                maxDataCalls = dataRegState.maxDataCalls;
-                cellIdentity = RILUtils.convertHalCellIdentity(dataRegState.cellIdentity);
             } else if (result instanceof android.hardware.radio.V1_4.DataRegStateResult) {
                 android.hardware.radio.V1_4.DataRegStateResult dataRegState =
                         (android.hardware.radio.V1_4.DataRegStateResult) result;
-                regState = getRegStateFromHalRegState(dataRegState.base.regState);
-                networkType = ServiceState.rilRadioTechnologyToNetworkType(dataRegState.base.rat);
-
-                reasonForDenial = dataRegState.base.reasonDataDenied;
-                emergencyOnly = isEmergencyOnly(dataRegState.base.regState);
-                maxDataCalls = dataRegState.base.maxDataCalls;
-                cellIdentity = RILUtils.convertHalCellIdentity(dataRegState.base.cellIdentity);
-                android.hardware.radio.V1_4.NrIndicators nrIndicators = dataRegState.nrIndicators;
-
+                LteVopsSupportInfo lteVopsSupportInfo;
                 // Check for lteVopsInfo only if its initialized and RAT is EUTRAN
                 if (dataRegState.vopsInfo.getDiscriminator() == hidl_discriminator.lteVopsInfo
                         && ServiceState.rilRadioTechnologyToAccessNetworkType(dataRegState.base.rat)
                             == AccessNetworkType.EUTRAN) {
                     android.hardware.radio.V1_4.LteVopsInfo vopsSupport =
                             dataRegState.vopsInfo.lteVopsInfo();
-                    lteVopsSupportInfo = convertHalLteVopsSupportInfo(vopsSupport.isVopsSupported,
-                        vopsSupport.isEmcBearerSupported);
+                    lteVopsSupportInfo = convertHalLteVopsSupportInfo(
+                            vopsSupport.isVopsSupported, vopsSupport.isEmcBearerSupported);
                 } else {
-                    lteVopsSupportInfo =
-                        new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
-                        LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
+                    lteVopsSupportInfo = new LteVopsSupportInfo(
+                            LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
+                            LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
                 }
-
-                isEndcAvailable = nrIndicators.isEndcAvailable;
-                isNrAvailable = nrIndicators.isNrAvailable;
-                isDcNrRestricted = nrIndicators.isDcNrRestricted;
+                int regState = getRegStateFromHalRegState(dataRegState.base.regState);
+                int networkType =
+                        ServiceState.rilRadioTechnologyToNetworkType(dataRegState.base.rat);
+                int reasonForDenial = dataRegState.base.reasonDataDenied;
+                boolean emergencyOnly = isEmergencyOnly(dataRegState.base.regState);
+                int maxDataCalls = dataRegState.base.maxDataCalls;
+                CellIdentity cellIdentity =
+                        RILUtils.convertHalCellIdentity(dataRegState.base.cellIdentity);
+                android.hardware.radio.V1_4.NrIndicators nrIndicators = dataRegState.nrIndicators;
+                boolean isEndcAvailable = nrIndicators.isEndcAvailable;
+                boolean isNrAvailable = nrIndicators.isNrAvailable;
+                boolean isDcNrRestricted = nrIndicators.isDcNrRestricted;
+                String rplmn = getPlmnFromCellIdentity(cellIdentity);
+                List<Integer> availableServices = getAvailableServices(
+                        regState, domain, emergencyOnly);
+                return new NetworkRegistrationInfo(domain, transportType, regState, networkType,
+                        reasonForDenial, emergencyOnly, availableServices, cellIdentity, rplmn,
+                        maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable,
+                        lteVopsSupportInfo);
             } else {
                 loge("Unknown type of DataRegStateResult " + result);
                 return null;
             }
-
-            String rplmn = getPlmnFromCellIdentity(cellIdentity);
-            List<Integer> availableServices = getAvailableServices(
-                    regState, domain, emergencyOnly);
-
-            return new NetworkRegistrationInfo(domain, transportType, regState, networkType,
-                    reasonForDenial, emergencyOnly, availableServices, cellIdentity, rplmn,
-                    maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable,
-                    lteVopsSupportInfo);
         }
 
         private @NonNull NetworkRegistrationInfo getNetworkRegistrationInfo(
@@ -805,6 +757,23 @@
         return new CellularNetworkServiceProvider(slotIndex);
     }
 
+    private boolean isMmsEnabled(Phone phone) {
+        CarrierConfigManager carrierConfigManager = phone.getContext()
+                .getSystemService(CarrierConfigManager.class);
+        if (carrierConfigManager != null) {
+            PersistableBundle config = carrierConfigManager.getConfigForSubId(
+                    phone.getSubId(), SmsManager.MMS_CONFIG_MMS_ENABLED);
+            if (config == null || config.isEmpty()) {
+                config = CarrierConfigManager.getDefaultConfig();
+            }
+
+            return config.getBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED);
+        } else {
+            loge("isMmsEnabled: CarrierConfigManager is null");
+            return false;
+        }
+    }
+
     private static void log(String s) {
         Rlog.d(TAG, s);
     }
diff --git a/src/java/com/android/internal/telephony/CommandException.java b/src/java/com/android/internal/telephony/CommandException.java
index e068c1c..6b6d84f 100644
--- a/src/java/com/android/internal/telephony/CommandException.java
+++ b/src/java/com/android/internal/telephony/CommandException.java
@@ -131,6 +131,17 @@
         BLOCKED_DUE_TO_CALL,
         RF_HARDWARE_ISSUE,
         NO_RF_CALIBRATION_INFO,
+        ENCODING_NOT_SUPPORTED,
+        FEATURE_NOT_SUPPORTED,
+        INVALID_CONTACT,
+        MODEM_INCOMPATIBLE,
+        NETWORK_TIMEOUT,
+        NO_SATELLITE_SIGNAL,
+        NOT_SUFFICIENT_ACCOUNT_BALANCE,
+        RADIO_TECHNOLOGY_NOT_SUPPORTED,
+        SUBSCRIBER_NOT_AUTHORIZED,
+        SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL,
+        UNIDENTIFIED_SUBSCRIBER
     }
 
     @UnsupportedAppUsage
@@ -341,6 +352,28 @@
                 return new CommandException(Error.RF_HARDWARE_ISSUE);
             case RILConstants.NO_RF_CALIBRATION_INFO:
                 return new CommandException(Error.NO_RF_CALIBRATION_INFO);
+            case RILConstants.ENCODING_NOT_SUPPORTED:
+                return new CommandException(Error.ENCODING_NOT_SUPPORTED);
+            case RILConstants.FEATURE_NOT_SUPPORTED:
+                return new CommandException(Error.FEATURE_NOT_SUPPORTED);
+            case RILConstants.INVALID_CONTACT:
+                return new CommandException(Error.INVALID_CONTACT);
+            case RILConstants.MODEM_INCOMPATIBLE:
+                return new CommandException(Error.MODEM_INCOMPATIBLE);
+            case RILConstants.NETWORK_TIMEOUT:
+                return new CommandException(Error.NETWORK_TIMEOUT);
+            case RILConstants.NO_SATELLITE_SIGNAL:
+                return new CommandException(Error.NO_SATELLITE_SIGNAL);
+            case RILConstants.NOT_SUFFICIENT_ACCOUNT_BALANCE:
+                return new CommandException(Error.NOT_SUFFICIENT_ACCOUNT_BALANCE);
+            case RILConstants.RADIO_TECHNOLOGY_NOT_SUPPORTED:
+                return new CommandException(Error.RADIO_TECHNOLOGY_NOT_SUPPORTED);
+            case RILConstants.SUBSCRIBER_NOT_AUTHORIZED:
+                return new CommandException(Error.SUBSCRIBER_NOT_AUTHORIZED);
+            case RILConstants.SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL:
+                return new CommandException(Error.SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL);
+            case RILConstants.UNIDENTIFIED_SUBSCRIBER:
+                return new CommandException(Error.UNIDENTIFIED_SUBSCRIBER);
             default:
                 Rlog.e("GSM", "Unrecognized RIL errno " + ril_errno);
                 return new CommandException(Error.INVALID_RESPONSE);
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 971e051..006a421 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -905,18 +905,6 @@
      *  ar.exception carries exception on failure
      *  ar.userObject contains the orignal value of result.obj
      *  ar.result contains a List of DataCallResponse
-     *  @deprecated Do not use.
-     */
-    @UnsupportedAppUsage
-    @Deprecated
-    void getPDPContextList(Message result);
-
-    /**
-     *  returned message
-     *  retMsg.obj = AsyncResult ar
-     *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
-     *  ar.result contains a List of DataCallResponse
      */
     @UnsupportedAppUsage
     void getDataCallList(Message result);
@@ -969,25 +957,6 @@
     void getIMSIForApp(String aid, Message result);
 
     /**
-     *  returned message
-     *  retMsg.obj = AsyncResult ar
-     *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
-     *  ar.result is String containing IMEI on success
-     */
-    void getIMEI(Message result);
-
-    /**
-     *  returned message
-     *  retMsg.obj = AsyncResult ar
-     *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
-     *  ar.result is String containing IMEISV on success
-     */
-    @UnsupportedAppUsage
-    void getIMEISV(Message result);
-
-    /**
      * Hang up one individual connection.
      *  returned message
      *  retMsg.obj = AsyncResult ar
@@ -1102,25 +1071,6 @@
      */
     void getLastCallFailCause (Message result);
 
-
-    /**
-     * Reason for last PDP context deactivate or failure to activate
-     * cause code returned as int[0] in Message.obj.response
-     * returns an integer cause code defined in TS 24.008
-     * section 6.1.3.1.3 or close approximation
-     * @deprecated Do not use.
-     */
-    @UnsupportedAppUsage
-    @Deprecated
-    void getLastPdpFailCause (Message result);
-
-    /**
-     * The preferred new alternative to getLastPdpFailCause
-     * that is also CDMA-compatible.
-     */
-    @UnsupportedAppUsage
-    void getLastDataCallFailCause (Message result);
-
     void setMute (boolean enableMute, Message response);
 
     void getMute (Message response);
@@ -1549,8 +1499,6 @@
      */
     void cancelPendingUssd (Message response);
 
-    void resetRadio(Message result);
-
     /**
      * Assign a specified band for RF configuration.
      *
@@ -1681,28 +1629,14 @@
     @UnsupportedAppUsage
     void reportStkServiceIsRunning(Message result);
 
-    @UnsupportedAppUsage
-    void invokeOemRilRequestRaw(byte[] data, Message response);
-
     /**
      * Sends carrier specific information to the vendor ril that can be used to
      * encrypt the IMSI and IMPI.
      *
-     * @param publicKey the public key of the carrier used to encrypt IMSI/IMPI.
-     * @param keyIdentifier the key identifier is optional information that is carrier
-     *        specific.
+     * @param imsiEncryptionInfo the IMSI encryption info
      * @param response callback message
      */
-    void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
-                                         Message response);
-
-    void invokeOemRilRequestStrings(String[] strings, Message response);
-
-    /**
-     * Fires when RIL_UNSOL_OEM_HOOK_RAW is received from the RIL.
-     */
-    void setOnUnsolOemHookRaw(Handler h, int what, Object obj);
-    void unSetOnUnsolOemHookRaw(Handler h);
+    void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo, Message response);
 
     /**
      * Send TERMINAL RESPONSE to the SIM, after processing a proactive command
@@ -1879,15 +1813,13 @@
     void queryTTYMode(Message response);
 
     /**
-     * Setup a packet data connection On successful completion, the result
+     * Setup a packet data connection. On successful completion, the result
      * message will return a SetupDataResult object containing the connection information.
      *
      * @param accessNetworkType
      *            Access network to use. Values is one of AccessNetworkConstants.AccessNetworkType.
      * @param dataProfile
      *            Data profile for data call setup
-     * @param isRoaming
-     *            Device is roaming or not
      * @param allowRoaming
      *            Flag indicating data roaming is enabled or not
      * @param reason
@@ -1918,9 +1850,9 @@
      * @param result
      *            Callback message
      */
-    void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
-            boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+    void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean allowRoaming,
+            int reason, LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo,
+            TrafficDescriptor trafficDescriptor,
             boolean matchAllRuleAllowed, Message result);
 
     /**
@@ -1982,22 +1914,6 @@
     public void getIccCardStatus(Message result);
 
     /**
-     * Request the status of all the physical UICC slots.
-     *
-     * @param result Callback message containing a {@link java.util.ArrayList} of
-     * {@link com.android.internal.telephony.uicc.IccSlotStatus} instances for all the slots.
-     */
-    void getIccSlotsStatus(Message result);
-
-    /**
-     * Set the mapping from logical slots to physical slots.
-     *
-     * @param physicalSlots Mapping from logical slots to physical slots.
-     * @param result Callback message is empty on completion.
-     */
-    void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result);
-
-    /**
      * Request the SIM application on the UICC to perform authentication
      * challenge/response algorithm. The data string and challenge response are
      * Base64 encoded Strings.
@@ -2074,24 +1990,20 @@
      *
      * @param dataProfile
      *            data profile for initial APN attach
-     * @param isRoaming
-     *            indicating the device is roaming or not
      * @param result
      *            callback message contains the information of SUCCESS/FAILURE
      */
-    void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result);
+    void setInitialAttachApn(DataProfile dataProfile, Message result);
 
     /**
      * Set data profiles in modem
      *
      * @param dps
      *            Array of the data profiles set to modem
-     * @param isRoaming
-     *            Indicating if the device is roaming or not
      * @param result
      *            callback message contains the information of SUCCESS/FAILURE
      */
-    void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result);
+    void setDataProfile(DataProfile[] dps, Message result);
 
     /**
      * Notifiy that we are testing an emergency call
@@ -2256,15 +2168,6 @@
             Message result);
 
     /**
-     * Whether the device modem supports reporting the EID in either the slot or card status or
-     * through ATR.
-     * @return true if the modem supports EID.
-     */
-    default boolean supportsEid() {
-        return false;
-    }
-
-    /**
      * Tells the modem if data is allowed or not.
      *
      * @param allowed
@@ -2318,34 +2221,6 @@
     public void unregisterForRadioCapabilityChanged(Handler h);
 
     /**
-     * Start LCE (Link Capacity Estimation) service with a desired reporting interval.
-     *
-     * @param reportIntervalMs
-     *        LCE info reporting interval (ms).
-     *
-     * @param result Callback message contains the current LCE status.
-     * {byte status, int actualIntervalMs}
-     */
-    public void startLceService(int reportIntervalMs, boolean pullMode, Message result);
-
-    /**
-     * Stop LCE service.
-     *
-     * @param result Callback message contains the current LCE status:
-     * {byte status, int actualIntervalMs}
-     *
-     */
-    public void stopLceService(Message result);
-
-    /**
-     * Pull LCE service for capacity data.
-     *
-     * @param result Callback message contains the capacity info:
-     * {int capacityKbps, byte confidenceLevel, byte lceSuspendedTemporarily}
-     */
-    public void pullLceData(Message result);
-
-    /**
      * Register a LCE info listener.
      *
      * @param h Handler for notification message.
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index e5a5c8f..c9db985 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -125,8 +125,10 @@
 
     @Override
     public void notifyDataActivity(Phone sender) {
+        int phoneId = sender.getPhoneId();
         int subId = sender.getSubId();
-        mTelephonyRegistryMgr.notifyDataActivityChanged(subId, sender.getDataActivityState());
+        mTelephonyRegistryMgr.notifyDataActivityChanged(phoneId, subId,
+                sender.getDataActivityState());
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index c2d0a0b..b0c1e52 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -107,6 +107,7 @@
 import com.android.internal.telephony.domainselection.DomainSelectionResolver;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.emergency.EmergencyStateTracker;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.gsm.GsmMmiCode;
 import com.android.internal.telephony.gsm.SsData;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
@@ -182,11 +183,6 @@
     private static final String VM_NUMBER_CDMA = "vm_number_key_cdma";
     public static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
     public static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer
-    private static final String PREFIX_WPS = "*272";
-    // WPS prefix when CLIR is being deactivated for the call.
-    private static final String PREFIX_WPS_CLIR_DEACTIVATE = "#31#*272";
-    // WPS prefix when CLIS is being activated for the call.
-    private static final String PREFIX_WPS_CLIR_ACTIVATE = "*31#*272";
     private CdmaSubscriptionSourceManager mCdmaSSM;
     public int mCdmaSubscriptionSource = CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN;
     private PowerManager.WakeLock mWakeLock;
@@ -304,23 +300,26 @@
     // Constructors
 
     public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId,
-                        int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory) {
-        this(context, ci, notifier, false, phoneId, precisePhoneType, telephonyComponentFactory);
+                        int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory,
+            @NonNull FeatureFlags featureFlags) {
+        this(context, ci, notifier, false, phoneId, precisePhoneType, telephonyComponentFactory,
+                featureFlags);
     }
 
     public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
                         boolean unitTestMode, int phoneId, int precisePhoneType,
-                        TelephonyComponentFactory telephonyComponentFactory) {
+                        TelephonyComponentFactory telephonyComponentFactory,
+            @NonNull FeatureFlags featureFlags) {
         this(context, ci, notifier,
                 unitTestMode, phoneId, precisePhoneType,
                 telephonyComponentFactory,
-                ImsManager::getInstance);
+                ImsManager::getInstance, featureFlags);
     }
 
     public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
             boolean unitTestMode, int phoneId, int precisePhoneType,
             TelephonyComponentFactory telephonyComponentFactory,
-            ImsManagerFactory imsManagerFactory) {
+            ImsManagerFactory imsManagerFactory, @NonNull FeatureFlags featureFlags) {
         super(precisePhoneType == PhoneConstants.PHONE_TYPE_GSM ? "GSM" : "CDMA",
                 notifier, context, ci, unitTestMode, phoneId, telephonyComponentFactory);
 
@@ -357,7 +356,7 @@
 
         mDataNetworkController = mTelephonyComponentFactory.inject(
                 DataNetworkController.class.getName())
-                .makeDataNetworkController(this, getLooper());
+                .makeDataNetworkController(this, getLooper(), featureFlags);
 
         mCarrierResolver = mTelephonyComponentFactory.inject(CarrierResolver.class.getName())
                 .makeCarrierResolver(this);
@@ -1414,9 +1413,7 @@
         }
 
         /** Check if the call is Wireless Priority Service call */
-        boolean isWpsCall = dialString != null ? (dialString.startsWith(PREFIX_WPS)
-                || dialString.startsWith(PREFIX_WPS_CLIR_ACTIVATE)
-                || dialString.startsWith(PREFIX_WPS_CLIR_DEACTIVATE)) : false;
+        boolean isWpsCall = PhoneNumberUtils.isWpsCallNumber(dialString);
 
         ImsPhone.ImsDialArgs.Builder imsDialArgsBuilder;
         imsDialArgsBuilder = ImsPhone.ImsDialArgs.Builder.from(dialArgs)
@@ -1863,15 +1860,15 @@
                 String spName = isPhoneTypeGsm() ? VM_NUMBER : VM_NUMBER_CDMA;
                 number = sp.getString(spName + getPhoneId(), null);
                 logd("getVoiceMailNumber: from " + spName + " number="
-                        + Rlog.pii(LOG_TAG, number));
+                        + Rlog.piiHandle(number));
             } else {
-                logd("getVoiceMailNumber: from IccRecords number=" + Rlog.pii(LOG_TAG, number));
+                logd("getVoiceMailNumber: from IccRecords number=" + Rlog.piiHandle(number));
             }
         }
         if (!isPhoneTypeGsm() && TextUtils.isEmpty(number)) {
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
             number = sp.getString(VM_NUMBER_CDMA + getPhoneId(), null);
-            logd("getVoiceMailNumber: from VM_NUMBER_CDMA number=" + number);
+            logd("getVoiceMailNumber: from VM_NUMBER_CDMA number=" + Rlog.piiHandle(number));
         }
 
         if (TextUtils.isEmpty(number)) {
@@ -1893,9 +1890,13 @@
                             && !mSST.isImsRegistered()) {
                         // roaming and IMS unregistered case if CC configured
                         number = defaultVmNumberRoamingAndImsUnregistered;
+                        logd("getVoiceMailNumber: from defaultVmNumberRoamingAndImsUnregistered "
+                                + "number=" + Rlog.piiHandle(number));
                     } else if (!TextUtils.isEmpty(defaultVmNumberRoaming)) {
                         // roaming default case if CC configured
                         number = defaultVmNumberRoaming;
+                        logd("getVoiceMailNumber: from defaultVmNumberRoaming number=" +
+                                Rlog.piiHandle(number));
                     }
                 }
             }
@@ -1909,12 +1910,13 @@
             if (b != null && b.getBoolean(
                     CarrierConfigManager.KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL)) {
                 number = getLine1Number();
+                logd("getVoiceMailNumber: from MSISDN number=" + Rlog.piiHandle(number));
             }
         }
-
         return number;
     }
 
+
     private String getVmSimImsi() {
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         return sp.getString(VM_SIM_IMSI + getPhoneId(), null);
@@ -3045,7 +3047,6 @@
         mCi.areUiccApplicationsEnabled(obtainMessage(EVENT_GET_UICC_APPS_ENABLEMENT_DONE));
 
         handleNullCipherEnabledChange();
-        startLceAfterRadioIsAvailable();
     }
 
     private void handleRadioOn() {
@@ -4407,7 +4408,6 @@
         } else {
             loge("deleteAndCreatePhone: newVoiceRadioTech=" + newVoiceRadioTech +
                     " is not CDMA or GSM (error) - aborting!");
-            return;
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 9b116b4..67dbdb5 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -39,6 +39,8 @@
 import com.android.ims.ImsManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.analytics.TelephonyAnalytics;
+import com.android.internal.telephony.analytics.TelephonyAnalytics.SmsMmsAnalytics;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.telephony.util.SMSDispatcherUtil;
@@ -242,6 +244,18 @@
                         tracker.mMessageId,
                         tracker.isFromDefaultSmsApplication(mContext),
                         tracker.getInterval());
+                if (mPhone != null) {
+                    TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+                    if (telephonyAnalytics != null) {
+                        SmsMmsAnalytics smsMmsAnalytics = telephonyAnalytics.getSmsMmsAnalytics();
+                        if (smsMmsAnalytics != null) {
+                            smsMmsAnalytics.onOutgoingSms(
+                                    true /* isOverIms */,
+                                    reason);
+                        }
+                    }
+                }
+
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -642,6 +656,18 @@
                     tracker.mMessageId,
                     tracker.isFromDefaultSmsApplication(mContext),
                     tracker.getInterval());
+            if (mPhone != null) {
+                TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+                if (telephonyAnalytics != null) {
+                    SmsMmsAnalytics smsMmsAnalytics = telephonyAnalytics.getSmsMmsAnalytics();
+                    if (smsMmsAnalytics != null) {
+                        smsMmsAnalytics.onOutgoingSms(
+                                true /* isOverIms */,
+                                SmsManager.RESULT_SYSTEM_ERROR
+                        );
+                    }
+                }
+            }
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 9166719..eafb4ba 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -71,6 +71,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.SmsConstants.MessageClass;
+import com.android.internal.telephony.analytics.TelephonyAnalytics;
+import com.android.internal.telephony.analytics.TelephonyAnalytics.SmsMmsAnalytics;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.util.NotificationChannelController;
 import com.android.internal.telephony.util.TelephonyUtils;
@@ -745,6 +747,16 @@
         if (result != Intents.RESULT_SMS_HANDLED && result != Activity.RESULT_OK) {
             mMetrics.writeIncomingSmsError(mPhone.getPhoneId(), is3gpp2(), smsSource, result);
             mPhone.getSmsStats().onIncomingSmsError(is3gpp2(), smsSource, result);
+            if (mPhone != null) {
+                TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+                if (telephonyAnalytics != null) {
+                    SmsMmsAnalytics smsMmsAnalytics = telephonyAnalytics.getSmsMmsAnalytics();
+                    if (smsMmsAnalytics != null) {
+                        smsMmsAnalytics.onIncomingSmsError(smsSource, result);
+                    }
+                }
+            }
+
         }
         return result;
     }
@@ -1008,6 +1020,16 @@
             logeWithLocalLog(errorMsg, tracker.getMessageId());
             mPhone.getSmsStats().onIncomingSmsError(
                     is3gpp2(), tracker.getSource(), RESULT_SMS_NULL_PDU);
+            if (mPhone != null) {
+                TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+                if (telephonyAnalytics != null) {
+                    SmsMmsAnalytics smsMmsAnalytics = telephonyAnalytics.getSmsMmsAnalytics();
+                    if (smsMmsAnalytics != null) {
+                        smsMmsAnalytics.onIncomingSmsError(
+                                tracker.getSource(), RESULT_SMS_NULL_PDU);
+                    }
+                }
+            }
             return false;
         }
 
@@ -1082,7 +1104,16 @@
                 format, timestamps, block, tracker.getMessageId());
         mPhone.getSmsStats().onIncomingSmsSuccess(is3gpp2(), tracker.getSource(),
                 messageCount, block, tracker.getMessageId());
+        if (mPhone != null) {
+            TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+            if (telephonyAnalytics != null) {
+                SmsMmsAnalytics smsMmsAnalytics = telephonyAnalytics.getSmsMmsAnalytics();
+                if (smsMmsAnalytics != null) {
+                    smsMmsAnalytics.onIncomingSmsSuccess(tracker.getSource());
+                }
+            }
 
+        }
         // Always invoke SMS filters, even if the number ends up being blocked, to prevent
         // surprising bugs due to blocking numbers that happen to be used for visual voicemail SMS
         // or other carrier system messages.
diff --git a/src/java/com/android/internal/telephony/MockModem.java b/src/java/com/android/internal/telephony/MockModem.java
index a20e748..83417c5 100644
--- a/src/java/com/android/internal/telephony/MockModem.java
+++ b/src/java/com/android/internal/telephony/MockModem.java
@@ -21,6 +21,7 @@
 import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
 import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
 import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SATELLITE;
 import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
 import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
 
@@ -44,6 +45,8 @@
     private static final String BIND_IRADIOVOICE = "android.telephony.mockmodem.iradiovoice";
     private static final String BIND_IRADIOIMS = "android.telephony.mockmodem.iradioims";
     private static final String BIND_IRADIOCONFIG = "android.telephony.mockmodem.iradioconfig";
+    private static final String BIND_IRADIOSATELLITE =
+            "android.telephony.mockmodem.iradiosatellite";
     private static final String PHONE_ID = "phone_id";
 
     private static final byte DEFAULT_PHONE_ID = 0x00;
@@ -65,6 +68,7 @@
     private IBinder mVoiceBinder;
     private IBinder mImsBinder;
     private IBinder mConfigBinder;
+    private IBinder mSatelliteBinder;
     private ServiceConnection mModemServiceConnection;
     private ServiceConnection mSimServiceConnection;
     private ServiceConnection mMessagingServiceConnection;
@@ -73,6 +77,7 @@
     private ServiceConnection mVoiceServiceConnection;
     private ServiceConnection mImsServiceConnection;
     private ServiceConnection mConfigServiceConnection;
+    private ServiceConnection mSatelliteServiceConnection;
 
     private byte mPhoneId;
     private String mTag;
@@ -115,6 +120,8 @@
                 mVoiceBinder = binder;
             } else if (mService == HAL_SERVICE_IMS) {
                 mImsBinder = binder;
+            } else if (mService == HAL_SERVICE_SATELLITE) {
+                mSatelliteBinder = binder;
             } else if (mService == RADIOCONFIG_SERVICE) {
                 mConfigBinder = binder;
             }
@@ -138,6 +145,8 @@
                 mVoiceBinder = null;
             } else if (mService == HAL_SERVICE_IMS) {
                 mImsBinder = null;
+            } else if (mService == HAL_SERVICE_SATELLITE) {
+                mSatelliteBinder = null;
             } else if (mService == RADIOCONFIG_SERVICE) {
                 mConfigBinder = null;
             }
@@ -179,6 +188,8 @@
                 return mVoiceBinder;
             case HAL_SERVICE_IMS:
                 return mImsBinder;
+            case HAL_SERVICE_SATELLITE:
+                return mSatelliteBinder;
             case RADIOCONFIG_SERVICE:
                 return mConfigBinder;
             default:
@@ -306,6 +317,20 @@
             } else {
                 Rlog.d(TAG, "IRadio Ims is bound");
             }
+        } else if (service == HAL_SERVICE_SATELLITE) {
+            if (mSatelliteBinder == null) {
+                mSatelliteServiceConnection = new MockModemConnection(HAL_SERVICE_SATELLITE);
+
+                boolean status =
+                        bindModuleToMockModemService(
+                                mPhoneId, BIND_IRADIOSATELLITE, mSatelliteServiceConnection);
+                if (!status) {
+                    Rlog.d(TAG, "IRadio Satellite bind fail");
+                    mSatelliteServiceConnection = null;
+                }
+            } else {
+                Rlog.d(TAG, "IRadio Satellite is bound");
+            }
         }
     }
 
@@ -368,6 +393,13 @@
                 mImsBinder = null;
                 Rlog.d(TAG, "unbind IRadio Ims");
             }
+        } else if (service == HAL_SERVICE_SATELLITE) {
+            if (mSatelliteServiceConnection != null) {
+                mContext.unbindService(mSatelliteServiceConnection);
+                mSatelliteServiceConnection = null;
+                mSatelliteBinder = null;
+                Rlog.d(TAG, "unbind IRadio Satellite");
+            }
         }
     }
 
@@ -391,6 +423,8 @@
                 return "voice";
             case HAL_SERVICE_IMS:
                 return "ims";
+            case HAL_SERVICE_SATELLITE:
+                return "satellite";
             case RADIOCONFIG_SERVICE:
                 return "config";
             default:
diff --git a/src/java/com/android/internal/telephony/NetworkIndication.java b/src/java/com/android/internal/telephony/NetworkIndication.java
index 7f9ff79..6025273 100644
--- a/src/java/com/android/internal/telephony/NetworkIndication.java
+++ b/src/java/com/android/internal/telephony/NetworkIndication.java
@@ -128,7 +128,7 @@
             android.hardware.radio.network.LinkCapacityEstimate lce) {
         mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
-        List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
+        List<LinkCapacityEstimate> response = RILUtils.convertHalLinkCapacityEstimate(lce);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
 
@@ -168,7 +168,10 @@
                 }
                 if (band == PhysicalChannelConfig.BAND_UNKNOWN) {
                     mRil.riljLoge("Unsupported unknown band.");
-                    return;
+                    // TODO, b/288310456,
+                    //  If the band is unknown, PhysicalChannelConfig can be built without setBand.
+                    //  It should be enforced not to allow "unknown" bands in the near future.
+                    // return;
                 } else {
                     builder.setBand(band);
                 }
@@ -204,9 +207,8 @@
             android.hardware.radio.network.SignalStrength signalStrength) {
         mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
-        SignalStrength ssInitial = RILUtils.convertHalSignalStrength(signalStrength);
+        SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength);
 
-        SignalStrength ss = mRil.fixupSignalStrength10(ssInitial);
         // Note this is set to "verbose" because it happens frequently
         if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
 
diff --git a/src/java/com/android/internal/telephony/NetworkResponse.java b/src/java/com/android/internal/telephony/NetworkResponse.java
index b1eb926..f26022e 100644
--- a/src/java/com/android/internal/telephony/NetworkResponse.java
+++ b/src/java/com/android/internal/telephony/NetworkResponse.java
@@ -25,12 +25,10 @@
 import android.telephony.BarringInfo;
 import android.telephony.CellInfo;
 import android.telephony.EmergencyRegResult;
-import android.telephony.LinkCapacityEstimate;
 import android.telephony.RadioAccessSpecifier;
 import android.telephony.SignalStrength;
 
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Interface declaring response functions to solicited radio requests for network APIs.
@@ -269,23 +267,6 @@
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
-     * @param lceInfo LceDataInfo indicating LCE data
-     */
-    public void pullLceDataResponse(RadioResponseInfo responseInfo,
-            android.hardware.radio.network.LceDataInfo lceInfo) {
-        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
-
-        if (rr != null) {
-            List<LinkCapacityEstimate> ret = RILUtils.convertHalLceData(lceInfo);
-            if (responseInfo.error == RadioError.NONE) {
-                RadioResponse.sendMessageResponse(rr.mResult, ret);
-            }
-            mRil.processResponseDone(rr, responseInfo, ret);
-        }
-    }
-
-    /**
-     * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setAllowedNetworkTypesBitmapResponse(RadioResponseInfo responseInfo) {
         RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
index 7567566..c09f8fb 100644
--- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -42,7 +42,9 @@
 import android.telephony.TelephonyScanManager;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.internal.util.ArrayUtils;
 
 import java.util.Collection;
 import java.util.List;
@@ -131,7 +133,7 @@
     }
 
     private boolean isValidScan(NetworkScanRequestInfo nsri) {
-        if (nsri.mRequest == null || nsri.mRequest.getSpecifiers() == null) {
+        if (nsri.mRequest == null || ArrayUtils.isEmpty(nsri.mRequest.getSpecifiers())) {
             return false;
         }
         if (nsri.mRequest.getSpecifiers().length > NetworkScanRequest.MAX_RADIO_ACCESS_NETWORKS) {
@@ -251,9 +253,10 @@
     /**
     * Tracks info about the radio network scan.
      *
-    * Also used to notice when the calling process dies so we can self-expire.
+    * Also used to notice when the calling process dies, so we can self-expire.
     */
-    class NetworkScanRequestInfo implements IBinder.DeathRecipient {
+    @VisibleForTesting
+    public class NetworkScanRequestInfo implements IBinder.DeathRecipient {
         private final NetworkScanRequest mRequest;
         private final Messenger mMessenger;
         private final IBinder mBinder;
@@ -265,8 +268,9 @@
         private final String mCallingPackage;
         private boolean mIsBinderDead;
 
-        NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id, Phone phone,
-                int callingUid, int callingPid, String callingPackage,
+        @VisibleForTesting
+        public NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id,
+                Phone phone, int callingUid, int callingPid, String callingPackage,
                 boolean renounceFineLocationAccess) {
             super();
             mRequest = r;
@@ -445,6 +449,10 @@
                 Log.e(TAG, "EVENT_RECEIVE_NETWORK_SCAN_RESULT: nsri is null");
                 return;
             }
+            if (nsri != mLiveRequestInfo) {
+                Log.e(TAG, "EVENT_RECEIVE_NETWORK_SCAN_RESULT received for inactive scan");
+                return;
+            }
             LocationAccessPolicy.LocationPermissionQuery locationQuery =
                     new LocationAccessPolicy.LocationPermissionQuery.Builder()
                     .setCallingPackage(nsri.mCallingPackage)
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index cee3a58..80e502c 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -84,6 +84,7 @@
 import com.android.ims.ImsManager;
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.analytics.TelephonyAnalytics;
 import com.android.internal.telephony.data.AccessNetworksManager;
 import com.android.internal.telephony.data.DataNetworkController;
 import com.android.internal.telephony.data.DataSettingsManager;
@@ -219,7 +220,6 @@
     private static final int EVENT_UNSOL_OEM_HOOK_RAW               = 34;
     protected static final int EVENT_GET_RADIO_CAPABILITY           = 35;
     protected static final int EVENT_SS                             = 36;
-    private static final int EVENT_CONFIG_LCE                       = 37;
     private static final int EVENT_CHECK_FOR_NETWORK_AUTOMATIC      = 38;
     protected static final int EVENT_VOICE_RADIO_TECH_CHANGED       = 39;
     protected static final int EVENT_REQUEST_VOICE_RADIO_TECH_DONE  = 40;
@@ -371,9 +371,6 @@
     private final AtomicReference<RadioCapability> mRadioCapability =
             new AtomicReference<RadioCapability>();
 
-    private static final int DEFAULT_REPORT_INTERVAL_MS = 200;
-    private static final boolean LCE_PULL_MODE = true;
-    private int mLceStatus = RILConstants.LCE_NOT_AVAILABLE;
     protected TelephonyComponentFactory mTelephonyComponentFactory;
 
     private int mPreferredUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
@@ -480,6 +477,7 @@
 
     protected VoiceCallSessionStats mVoiceCallSessionStats;
     protected SmsStats mSmsStats;
+    protected TelephonyAnalytics mTelephonyAnalytics;
 
     protected LinkBandwidthEstimator mLinkBandwidthEstimator;
 
@@ -649,8 +647,10 @@
         if (getPhoneType() != PhoneConstants.PHONE_TYPE_SIP) {
             mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
         }
-        mCi.startLceService(DEFAULT_REPORT_INTERVAL_MS, LCE_PULL_MODE,
-                obtainMessage(EVENT_CONFIG_LCE));
+        //Initialize Telephony Analytics
+        if (isTelephonyAnalyticsEnabled()) {
+            mTelephonyAnalytics = new TelephonyAnalytics(this);
+        }
     }
 
     /**
@@ -808,16 +808,6 @@
                 // deprecated, ignore
                 break;
 
-            case EVENT_CONFIG_LCE:
-                ar = (AsyncResult) msg.obj;
-                if (ar.exception != null) {
-                    Rlog.d(LOG_TAG, "config LCE service failed: " + ar.exception);
-                } else {
-                    final ArrayList<Integer> statusInfo = (ArrayList<Integer>)ar.result;
-                    mLceStatus = statusInfo.get(0);
-                }
-                break;
-
             case EVENT_CHECK_FOR_NETWORK_AUTOMATIC: {
                 onCheckForNetworkSelectionModeAutomatic(msg);
                 break;
@@ -2721,47 +2711,6 @@
     }
 
     /**
-     * Invokes RIL_REQUEST_OEM_HOOK_RAW on RIL implementation.
-     *
-     * @param data The data for the request.
-     * @param response <strong>On success</strong>,
-     * (byte[])(((AsyncResult)response.obj).result)
-     * <strong>On failure</strong>,
-     * (((AsyncResult)response.obj).result) == null and
-     * (((AsyncResult)response.obj).exception) being an instance of
-     * com.android.internal.telephony.gsm.CommandException
-     *
-     * @see #invokeOemRilRequestRaw(byte[], android.os.Message)
-     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
-     */
-    @UnsupportedAppUsage
-    @Deprecated
-    public void invokeOemRilRequestRaw(byte[] data, Message response) {
-        mCi.invokeOemRilRequestRaw(data, response);
-    }
-
-    /**
-     * Invokes RIL_REQUEST_OEM_HOOK_Strings on RIL implementation.
-     *
-     * @param strings The strings to make available as the request data.
-     * @param response <strong>On success</strong>, "response" bytes is
-     * made available as:
-     * (String[])(((AsyncResult)response.obj).result).
-     * <strong>On failure</strong>,
-     * (((AsyncResult)response.obj).result) == null and
-     * (((AsyncResult)response.obj).exception) being an instance of
-     * com.android.internal.telephony.gsm.CommandException
-     *
-     * @see #invokeOemRilRequestStrings(java.lang.String[], android.os.Message)
-     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
-     */
-    @UnsupportedAppUsage
-    @Deprecated
-    public void invokeOemRilRequestStrings(String[] strings, Message response) {
-        mCi.invokeOemRilRequestStrings(strings, response);
-    }
-
-    /**
      * Read one of the NV items defined in {@link RadioNVItems} / {@code ril_nv_items.h}.
      * Used for device configuration by some CDMA operators.
      *
@@ -4417,12 +4366,15 @@
             subInfo = subInfoInternal.toSubscriptionInfo();
         }
 
-        if (subInfo == null
-                || subInfo.getUsageSetting() == SubscriptionManager.USAGE_SETTING_UNKNOWN) {
+        if (subInfo == null) {
             loge("Failed to get SubscriptionInfo for subId=" + subId);
             return SubscriptionManager.USAGE_SETTING_UNKNOWN;
         }
 
+        if (subInfo.getUsageSetting() == SubscriptionManager.USAGE_SETTING_UNKNOWN) {
+            return SubscriptionManager.USAGE_SETTING_UNKNOWN;
+        }
+
         if (subInfo.getUsageSetting() != SubscriptionManager.USAGE_SETTING_DEFAULT) {
             return subInfo.getUsageSetting();
         }
@@ -4524,13 +4476,6 @@
     }
 
     /**
-     * Returns the status of Link Capacity Estimation (LCE) service.
-     */
-    public int getLceStatus() {
-        return mLceStatus;
-    }
-
-    /**
      * Returns the modem activity information
      */
     public void getModemActivityInfo(Message response, WorkSource workSource)  {
@@ -4538,15 +4483,6 @@
     }
 
     /**
-     * Starts LCE service after radio becomes available.
-     * LCE service state may get destroyed on the modem when radio becomes unavailable.
-     */
-    public void startLceAfterRadioIsAvailable() {
-        mCi.startLceService(DEFAULT_REPORT_INTERVAL_MS, LCE_PULL_MODE,
-                obtainMessage(EVENT_CONFIG_LCE));
-    }
-
-    /**
      * Control the data throttling at modem.
      *
      * @param result Message that will be sent back to the requester
@@ -4805,6 +4741,16 @@
         mSmsStats = smsStats;
     }
 
+    /** Getter for Telephony Analytics */
+    public TelephonyAnalytics getTelephonyAnalytics() {
+        return mTelephonyAnalytics;
+    }
+
+    public boolean isTelephonyAnalyticsEnabled() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.telephony_analytics_switch);
+    }
+
     /** @hide */
     public CarrierPrivilegesTracker getCarrierPrivilegesTracker() {
         return null;
@@ -5705,6 +5651,13 @@
             pw.flush();
             pw.println("++++++++++++++++++++++++++++++++");
         }
+        if (mTelephonyAnalytics != null) {
+            try {
+                mTelephonyAnalytics.dump(fd, pw, args);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
     }
 
     private void logd(String s) {
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 57a375b..e49804f 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -23,6 +23,7 @@
 
 import static java.util.Arrays.copyOf;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
@@ -47,6 +48,8 @@
 import com.android.internal.telephony.data.TelephonyNetworkFactory;
 import com.android.internal.telephony.euicc.EuiccCardController;
 import com.android.internal.telephony.euicc.EuiccController;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneFactory;
 import com.android.internal.telephony.metrics.MetricsCollector;
@@ -102,11 +105,17 @@
     static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>();
     private static MetricsCollector sMetricsCollector;
     private static RadioInterfaceCapabilityController sRadioHalCapabilities;
+    private static @NonNull FeatureFlags sFeatureFlags = new FeatureFlagsImpl();
 
     //***** Class Methods
 
-    public static void makeDefaultPhones(Context context) {
-        makeDefaultPhone(context);
+    /**
+     * @param context The context.
+     * @param featureFlags The feature flag.
+     */
+    public static void makeDefaultPhones(Context context, @NonNull FeatureFlags featureFlags) {
+        sFeatureFlags = featureFlags;
+        makeDefaultPhone(context, featureFlags);
     }
 
     /**
@@ -114,7 +123,7 @@
      * instances
      */
     @UnsupportedAppUsage
-    public static void makeDefaultPhone(Context context) {
+    public static void makeDefaultPhone(Context context, @NonNull FeatureFlags featureFlags) {
         synchronized (sLockProxyPhones) {
             if (!sMadeDefaults) {
                 sContext = context;
@@ -198,7 +207,7 @@
 
                 Rlog.i(LOG_TAG, "Creating SubscriptionManagerService");
                 sSubscriptionManagerService = new SubscriptionManagerService(context,
-                        Looper.myLooper());
+                        Looper.myLooper(), featureFlags);
 
                 TelephonyComponentFactory.getInstance().inject(MultiSimSettingController.class.
                         getName()).initMultiSimSettingController(context);
@@ -318,7 +327,7 @@
 
         return injectedComponentFactory.makePhone(context,
                 sCommandsInterfaces[phoneId], sPhoneNotifier, phoneId, phoneType,
-                TelephonyComponentFactory.getInstance());
+                TelephonyComponentFactory.getInstance(), sFeatureFlags);
     }
 
     @UnsupportedAppUsage
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 5ecdfcb..826af3f 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -22,6 +22,7 @@
 import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
 import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
 import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SATELLITE;
 import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
 import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
 
@@ -58,13 +59,6 @@
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.BarringInfo;
 import android.telephony.CarrierRestrictionRules;
-import android.telephony.CellInfo;
-import android.telephony.CellSignalStrengthCdma;
-import android.telephony.CellSignalStrengthGsm;
-import android.telephony.CellSignalStrengthLte;
-import android.telephony.CellSignalStrengthNr;
-import android.telephony.CellSignalStrengthTdscdma;
-import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.ClientRequestStats;
 import android.telephony.DomainSelectionService;
 import android.telephony.ImsiEncryptionInfo;
@@ -73,8 +67,6 @@
 import android.telephony.NetworkScanRequest;
 import android.telephony.RadioAccessFamily;
 import android.telephony.RadioAccessSpecifier;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
 import android.telephony.SignalThresholdInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyHistogram;
@@ -104,6 +96,7 @@
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.telephony.uicc.SimPhonebookRecord;
 import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.internal.util.FunctionalUtils;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -123,7 +116,6 @@
 
 /**
  * RIL implementation of the CommandsInterface.
- *
  * {@hide}
  */
 public class RIL extends BaseCommands implements CommandsInterface {
@@ -136,8 +128,7 @@
     static final int RIL_HISTOGRAM_BUCKET_COUNT = 5;
 
     /**
-     * Wake lock timeout should be longer than the longest timeout in
-     * the vendor ril.
+     * Wake lock timeout should be longer than the longest timeout in the vendor ril.
      */
     private static final int DEFAULT_WAKE_LOCK_TIMEOUT_MS = 60000;
 
@@ -159,18 +150,6 @@
     public static final HalVersion RADIO_HAL_VERSION_UNKNOWN = HalVersion.UNKNOWN;
 
     /** @hide */
-    public static final HalVersion RADIO_HAL_VERSION_1_0 = new HalVersion(1, 0);
-
-    /** @hide */
-    public static final HalVersion RADIO_HAL_VERSION_1_1 = new HalVersion(1, 1);
-
-    /** @hide */
-    public static final HalVersion RADIO_HAL_VERSION_1_2 = new HalVersion(1, 2);
-
-    /** @hide */
-    public static final HalVersion RADIO_HAL_VERSION_1_3 = new HalVersion(1, 3);
-
-    /** @hide */
     public static final HalVersion RADIO_HAL_VERSION_1_4 = new HalVersion(1, 4);
 
     /** @hide */
@@ -185,8 +164,11 @@
     /** @hide */
     public static final HalVersion RADIO_HAL_VERSION_2_1 = new HalVersion(2, 1);
 
+    /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_2_2 = new HalVersion(2, 2);
+
     // Hal version
-    private Map<Integer, HalVersion> mHalVersion = new HashMap<>();
+    private final Map<Integer, HalVersion> mHalVersion = new HashMap<>();
 
     //***** Instance Variables
 
@@ -197,8 +179,7 @@
     public final WakeLock mAckWakeLock;        // Wake lock associated with ack sent
     final int mWakeLockTimeout;         // Timeout associated with request/response
     final int mAckWakeLockTimeout;      // Timeout associated with ack sent
-    // The number of wakelock requests currently active.  Don't release the lock
-    // until dec'd to 0
+    // The number of wakelock requests currently active. Don't release the lock until dec'd to 0.
     int mWakeLockCount;
 
     // Variables used to identify releasing of WL on wakelock timeouts
@@ -226,7 +207,7 @@
 
     public static final int MIN_SERVICE_IDX = HAL_SERVICE_RADIO;
 
-    public static final int MAX_SERVICE_IDX = HAL_SERVICE_IMS;
+    public static final int MAX_SERVICE_IDX = HAL_SERVICE_SATELLITE;
 
     /**
      * An array of sets that records if services are disabled in the HAL for a specific phone ID
@@ -261,6 +242,8 @@
     private ModemIndication mModemIndication;
     private NetworkResponse mNetworkResponse;
     private NetworkIndication mNetworkIndication;
+    private SatelliteResponse mSatelliteResponse;
+    private SatelliteIndication mSatelliteIndication;
     private SimResponse mSimResponse;
     private SimIndication mSimIndication;
     private VoiceResponse mVoiceResponse;
@@ -331,11 +314,10 @@
                             }
                             if (RILJ_LOGD) {
                                 int count = mRequestList.size();
-                                Rlog.d(RILJ_LOG_TAG, "WAKE_LOCK_TIMEOUT " +
-                                        " mRequestList=" + count);
+                                riljLog("WAKE_LOCK_TIMEOUT mRequestList=" + count);
                                 for (int i = 0; i < count; i++) {
                                     rr = mRequestList.valueAt(i);
-                                    Rlog.d(RILJ_LOG_TAG, i + ": [" + rr.mSerial + "] "
+                                    riljLog(i + ": [" + rr.mSerial + "] "
                                             + RILUtils.requestToString(rr.mRequest));
                                 }
                             }
@@ -346,7 +328,7 @@
                 case EVENT_ACK_WAKE_LOCK_TIMEOUT:
                     if (msg.arg1 == mAckWlSequenceNum && clearWakeLock(FOR_ACK_WAKELOCK)) {
                         if (RILJ_LOGV) {
-                            Rlog.d(RILJ_LOG_TAG, "ACK_WAKE_LOCK_TIMEOUT");
+                            riljLog("ACK_WAKE_LOCK_TIMEOUT");
                         }
                     }
                     break;
@@ -493,9 +475,9 @@
         clearRequestList(RADIO_NOT_AVAILABLE, false);
 
         if (service == HAL_SERVICE_RADIO) {
-            getRadioProxy(null);
+            getRadioProxy();
         } else {
-            getRadioServiceProxy(service, null);
+            getRadioServiceProxy(service);
         }
     }
 
@@ -605,10 +587,10 @@
     @VisibleForTesting
     public void setCompatVersion(int rilRequest, @NonNull HalVersion halVersion) {
         HalVersion oldVersion = getCompatVersion(rilRequest);
-        // Do not allow to set same or greater verions
+        // Do not allow to set same or greater versions
         if (oldVersion != null && halVersion.greaterOrEqual(oldVersion)) {
-            riljLoge("setCompatVersion with equal or greater one, ignored, halVerion=" + halVersion
-                    + ", oldVerion=" + oldVersion);
+            riljLoge("setCompatVersion with equal or greater one, ignored, halVersion=" + halVersion
+                    + ", oldVersion=" + oldVersion);
             return;
         }
         mCompatOverrides.put(rilRequest, halVersion);
@@ -622,7 +604,7 @@
 
     /** Returns a {@link IRadio} instance or null if the service is not available. */
     @VisibleForTesting
-    public synchronized IRadio getRadioProxy(Message result) {
+    public synchronized IRadio getRadioProxy() {
         if (mHalVersion.containsKey(HAL_SERVICE_RADIO)
                 && mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
             return null;
@@ -630,11 +612,6 @@
         if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return null;
         if (!mIsCellularSupported) {
             if (RILJ_LOGV) riljLog("getRadioProxy: Not calling getService(): wifi-only");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
-                result.sendToTarget();
-            }
             return null;
         }
 
@@ -673,39 +650,7 @@
                 }
 
                 if (mRadioProxy == null) {
-                    try {
-                        mRadioProxy = android.hardware.radio.V1_3.IRadio.getService(
-                                HIDL_SERVICE_NAME[mPhoneId], true);
-                        mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_3);
-                    } catch (NoSuchElementException e) {
-                    }
-                }
-
-                if (mRadioProxy == null) {
-                    try {
-                        mRadioProxy = android.hardware.radio.V1_2.IRadio.getService(
-                                HIDL_SERVICE_NAME[mPhoneId], true);
-                        mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_2);
-                    } catch (NoSuchElementException e) {
-                    }
-                }
-
-                if (mRadioProxy == null) {
-                    try {
-                        mRadioProxy = android.hardware.radio.V1_1.IRadio.getService(
-                                HIDL_SERVICE_NAME[mPhoneId], true);
-                        mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_1);
-                    } catch (NoSuchElementException e) {
-                    }
-                }
-
-                if (mRadioProxy == null) {
-                    try {
-                        mRadioProxy = android.hardware.radio.V1_0.IRadio.getService(
-                                HIDL_SERVICE_NAME[mPhoneId], true);
-                        mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_0);
-                    } catch (NoSuchElementException e) {
-                    }
+                    riljLoge("IRadio <1.4 is no longer supported.");
                 }
 
                 if (mRadioProxy != null) {
@@ -729,11 +674,6 @@
         if (mRadioProxy == null) {
             // getService() is a blocking call, so this should never happen
             riljLoge("getRadioProxy: mRadioProxy == null");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
-                result.sendToTarget();
-            }
         }
 
         return mRadioProxy;
@@ -742,31 +682,33 @@
     /**
      * Returns a {@link RadioDataProxy}, {@link RadioMessagingProxy}, {@link RadioModemProxy},
      * {@link RadioNetworkProxy}, {@link RadioSimProxy}, {@link RadioVoiceProxy},
-     * {@link RadioImsProxy}, or null if the service is not available.
+     * {@link RadioImsProxy}, {@link RadioSatelliteProxy}, or null if the service is not available.
      */
     @NonNull
-    public <T extends RadioServiceProxy> T getRadioServiceProxy(Class<T> serviceClass,
-            Message result) {
+    public <T extends RadioServiceProxy> T getRadioServiceProxy(Class<T> serviceClass) {
         if (serviceClass == RadioDataProxy.class) {
-            return (T) getRadioServiceProxy(HAL_SERVICE_DATA, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_DATA);
         }
         if (serviceClass == RadioMessagingProxy.class) {
-            return (T) getRadioServiceProxy(HAL_SERVICE_MESSAGING, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_MESSAGING);
         }
         if (serviceClass == RadioModemProxy.class) {
-            return (T) getRadioServiceProxy(HAL_SERVICE_MODEM, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_MODEM);
         }
         if (serviceClass == RadioNetworkProxy.class) {
-            return (T) getRadioServiceProxy(HAL_SERVICE_NETWORK, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_NETWORK);
         }
         if (serviceClass == RadioSimProxy.class) {
-            return (T) getRadioServiceProxy(HAL_SERVICE_SIM, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_SIM);
         }
         if (serviceClass == RadioVoiceProxy.class) {
-            return (T) getRadioServiceProxy(HAL_SERVICE_VOICE, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_VOICE);
         }
         if (serviceClass == RadioImsProxy.class) {
-            return (T) getRadioServiceProxy(HAL_SERVICE_IMS, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_IMS);
+        }
+        if (serviceClass == RadioSatelliteProxy.class) {
+            return (T) getRadioServiceProxy(HAL_SERVICE_SATELLITE);
         }
         riljLoge("getRadioServiceProxy: unrecognized " + serviceClass);
         return null;
@@ -778,18 +720,13 @@
      */
     @VisibleForTesting
     @NonNull
-    public synchronized RadioServiceProxy getRadioServiceProxy(int service, Message result) {
+    public synchronized RadioServiceProxy getRadioServiceProxy(int service) {
         if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return mServiceProxies.get(service);
         if ((service >= HAL_SERVICE_IMS) && !isRadioServiceSupported(service)) {
             return mServiceProxies.get(service);
         }
         if (!mIsCellularSupported) {
             if (RILJ_LOGV) riljLog("getRadioServiceProxy: Not calling getService(): wifi-only");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
-                result.sendToTarget();
-            }
             return mServiceProxies.get(service);
         }
 
@@ -910,6 +847,21 @@
                                             .asInterface(binder)));
                         }
                         break;
+                    case HAL_SERVICE_SATELLITE:
+                        if (mMockModem == null) {
+                            binder = ServiceManager.waitForDeclaredService(
+                                    android.hardware.radio.satellite.IRadioSatellite.DESCRIPTOR
+                                            + "/" + HIDL_SERVICE_NAME[mPhoneId]);
+                        } else {
+                            binder = mMockModem.getServiceBinder(HAL_SERVICE_SATELLITE);
+                        }
+                        if (binder != null) {
+                            mHalVersion.put(service, ((RadioSatelliteProxy) serviceProxy).setAidl(
+                                    mHalVersion.get(service),
+                                    android.hardware.radio.satellite.IRadioSatellite.Stub
+                                            .asInterface(binder)));
+                        }
+                        break;
                 }
 
                 if (serviceProxy.isEmpty()
@@ -947,46 +899,7 @@
 
                 if (serviceProxy.isEmpty()
                         && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
-                    try {
-                        mHalVersion.put(service, RADIO_HAL_VERSION_1_3);
-                        serviceProxy.setHidl(mHalVersion.get(service),
-                                android.hardware.radio.V1_3.IRadio.getService(
-                                        HIDL_SERVICE_NAME[mPhoneId], true));
-                    } catch (NoSuchElementException e) {
-                    }
-                }
-
-                if (serviceProxy.isEmpty()
-                        && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
-                    try {
-                        mHalVersion.put(service, RADIO_HAL_VERSION_1_2);
-                        serviceProxy.setHidl(mHalVersion.get(service),
-                                android.hardware.radio.V1_2.IRadio.getService(
-                                        HIDL_SERVICE_NAME[mPhoneId], true));
-                    } catch (NoSuchElementException e) {
-                    }
-                }
-
-                if (serviceProxy.isEmpty()
-                        && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
-                    try {
-                        mHalVersion.put(service, RADIO_HAL_VERSION_1_1);
-                        serviceProxy.setHidl(mHalVersion.get(service),
-                                android.hardware.radio.V1_1.IRadio.getService(
-                                        HIDL_SERVICE_NAME[mPhoneId], true));
-                    } catch (NoSuchElementException e) {
-                    }
-                }
-
-                if (serviceProxy.isEmpty()
-                        && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
-                    try {
-                        mHalVersion.put(service, RADIO_HAL_VERSION_1_0);
-                        serviceProxy.setHidl(mHalVersion.get(service),
-                                android.hardware.radio.V1_0.IRadio.getService(
-                                        HIDL_SERVICE_NAME[mPhoneId], true));
-                    } catch (NoSuchElementException e) {
-                    }
+                    riljLoge("IRadio <1.4 is no longer supported.");
                 }
 
                 if (!serviceProxy.isEmpty()) {
@@ -1034,10 +947,15 @@
                                 ((RadioImsProxy) serviceProxy).getAidl().setResponseFunctions(
                                         mImsResponse, mImsIndication);
                                 break;
+                            case HAL_SERVICE_SATELLITE:
+                                mDeathRecipients.get(service).linkToDeath(
+                                        ((RadioSatelliteProxy) serviceProxy).getAidl().asBinder());
+                                ((RadioSatelliteProxy) serviceProxy).getAidl().setResponseFunctions(
+                                        mSatelliteResponse, mSatelliteIndication);
+                                break;
                         }
                     } else {
-                        if (mHalVersion.get(service)
-                                .greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+                        if (mHalVersion.get(service).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
                             throw new AssertionError("serviceProxy shouldn't be HIDL with HAL 2.0");
                         }
                         if (!mIsRadioProxyInitialized) {
@@ -1063,11 +981,6 @@
         if (serviceProxy.isEmpty()) {
             // getService() is a blocking call, so this should never happen
             riljLoge("getRadioServiceProxy: serviceProxy == null");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
-                result.sendToTarget();
-            }
         }
 
         return serviceProxy;
@@ -1080,9 +993,9 @@
             if (active) {
                 // Try to connect to RIL services and set response functions.
                 if (service == HAL_SERVICE_RADIO) {
-                    getRadioProxy(null);
+                    getRadioProxy();
                 } else {
-                    getRadioServiceProxy(service, null);
+                    getRadioServiceProxy(service);
                 }
             } else {
                 resetProxyAndRequestList(service);
@@ -1148,6 +1061,8 @@
         mModemIndication = new ModemIndication(this);
         mNetworkResponse = new NetworkResponse(this);
         mNetworkIndication = new NetworkIndication(this);
+        mSatelliteResponse = new SatelliteResponse(this);
+        mSatelliteIndication = new SatelliteIndication(this);
         mSimResponse = new SimResponse(this);
         mSimIndication = new SimIndication(this);
         mVoiceResponse = new VoiceResponse(this);
@@ -1182,6 +1097,7 @@
             mServiceProxies.put(HAL_SERVICE_SIM, new RadioSimProxy());
             mServiceProxies.put(HAL_SERVICE_VOICE, new RadioVoiceProxy());
             mServiceProxies.put(HAL_SERVICE_IMS, new RadioImsProxy());
+            mServiceProxies.put(HAL_SERVICE_SATELLITE, new RadioSatelliteProxy());
         } else {
             mServiceProxies = proxies;
         }
@@ -1211,11 +1127,11 @@
         // wakelock stuff is initialized above as callbacks are received on separate binder threads)
         for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
             if (service == HAL_SERVICE_RADIO) {
-                getRadioProxy(null);
+                getRadioProxy();
             } else {
                 if (proxies == null) {
                     // Prevent telephony tests from calling the service
-                    getRadioServiceProxy(service, null);
+                    getRadioServiceProxy(service);
                 }
             }
 
@@ -1264,6 +1180,9 @@
             case HAL_SERVICE_IMS:
                 serviceName = android.hardware.radio.ims.IRadioIms.DESCRIPTOR;
                 break;
+            case HAL_SERVICE_SATELLITE:
+                serviceName = android.hardware.radio.satellite.IRadioSatellite.DESCRIPTOR;
+                break;
         }
 
         if (!serviceName.equals("")
@@ -1319,45 +1238,81 @@
         resetProxyAndRequestList(service);
     }
 
+    private void radioServiceInvokeHelper(int service, RILRequest rr, String methodName,
+            FunctionalUtils.ThrowingRunnable helper) {
+        try {
+            helper.runOrThrow();
+        } catch (RuntimeException e) {
+            riljLoge(methodName + " RuntimeException: " + e);
+            int error = RadioError.SYSTEM_ERR;
+            int responseType = RadioResponseType.SOLICITED;
+            processResponseInternal(service, rr.mSerial, error, responseType);
+            processResponseDoneInternal(rr, error, responseType, null);
+        } catch (Exception e) {
+            handleRadioProxyExceptionForRR(service, methodName, e);
+        }
+    }
+
+    private boolean canMakeRequest(String request, RadioServiceProxy proxy, Message result,
+            HalVersion version) {
+        int service = HAL_SERVICE_RADIO;
+        if (proxy instanceof RadioDataProxy) {
+            service = HAL_SERVICE_DATA;
+        } else if (proxy instanceof RadioMessagingProxy) {
+            service = HAL_SERVICE_MESSAGING;
+        } else if (proxy instanceof RadioModemProxy) {
+            service = HAL_SERVICE_MODEM;
+        } else if (proxy instanceof RadioNetworkProxy) {
+            service = HAL_SERVICE_NETWORK;
+        } else if (proxy instanceof RadioSimProxy) {
+            service = HAL_SERVICE_SIM;
+        } else if (proxy instanceof RadioVoiceProxy) {
+            service = HAL_SERVICE_VOICE;
+        } else if (proxy instanceof RadioImsProxy) {
+            service = HAL_SERVICE_IMS;
+        } else if (proxy instanceof RadioSatelliteProxy) {
+            service = HAL_SERVICE_SATELLITE;
+        }
+
+        if (mHalVersion.get(service).less(version)) {
+            riljLoge(String.format("%s not supported on service %s < %s.",
+                    request, serviceToString(service), version));
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+            return false;
+        }
+        if (proxy == null || proxy.isEmpty()) {
+            riljLoge(String.format("Unable to complete %s because service %s is not available.",
+                    request, serviceToString(service)));
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+                result.sendToTarget();
+            }
+            return false;
+        }
+        return true;
+    }
+
     @Override
     public void getIccCardStatus(Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_STATUS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                simProxy.getIccCardStatus(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getIccCardStatus", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("getIccCardStatus", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
-    }
 
-    @Override
-    public void getIccSlotsStatus(Message result) {
-        if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "getIccSlotsStatus: REQUEST_NOT_SUPPORTED");
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
-        }
-    }
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_STATUS, result, mRILDefaultWorkSource);
 
-    @Override
-    public void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result) {
         if (RILJ_LOGD) {
-            Rlog.d(RILJ_LOG_TAG, "setLogicalToPhysicalSlotMapping: REQUEST_NOT_SUPPORTED");
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
         }
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
-        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "getIccCardStatus", () -> {
+            simProxy.getIccCardStatus(rr.mSerial);
+        });
     }
 
     @Override
@@ -1367,22 +1322,22 @@
 
     @Override
     public void supplyIccPinForApp(String pin, String aid, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PIN, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " aid = " + aid);
-            }
-
-            try {
-                simProxy.supplyIccPinForApp(rr.mSerial, RILUtils.convertNullToEmptyString(pin),
-                        RILUtils.convertNullToEmptyString(aid));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPinForApp", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("supplyIccPinForApp", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PIN, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " aid = " + aid);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "supplyIccPinForApp", () -> {
+            simProxy.supplyIccPinForApp(rr.mSerial, RILUtils.convertNullToEmptyString(pin),
+                    RILUtils.convertNullToEmptyString(aid));
+        });
     }
 
     @Override
@@ -1392,24 +1347,24 @@
 
     @Override
     public void supplyIccPukForApp(String puk, String newPin, String aid, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PUK, result, mRILDefaultWorkSource);
-
-            String pukStr = RILUtils.convertNullToEmptyString(puk);
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " isPukEmpty = " + pukStr.isEmpty() + " aid = " + aid);
-            }
-
-            try {
-                simProxy.supplyIccPukForApp(rr.mSerial, pukStr,
-                        RILUtils.convertNullToEmptyString(newPin),
-                        RILUtils.convertNullToEmptyString(aid));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPukForApp", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("supplyIccPukForApp", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PUK, result, mRILDefaultWorkSource);
+
+        String pukStr = RILUtils.convertNullToEmptyString(puk);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " isPukEmpty = " + pukStr.isEmpty() + " aid = " + aid);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "supplyIccPukForApp", () -> {
+            simProxy.supplyIccPukForApp(rr.mSerial, pukStr,
+                    RILUtils.convertNullToEmptyString(newPin),
+                    RILUtils.convertNullToEmptyString(aid));
+        });
     }
 
     @Override
@@ -1419,23 +1374,22 @@
 
     @Override
     public void supplyIccPin2ForApp(String pin, String aid, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PIN2, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " aid = " + aid);
-            }
-
-            try {
-                simProxy.supplyIccPin2ForApp(rr.mSerial, RILUtils.convertNullToEmptyString(pin),
-                        RILUtils.convertNullToEmptyString(aid));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPin2ForApp", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("supplyIccPin2ForApp", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PIN2, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " aid = " + aid);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "supplyIccPin2ForApp", () -> {
+            simProxy.supplyIccPin2ForApp(rr.mSerial, RILUtils.convertNullToEmptyString(pin),
+                    RILUtils.convertNullToEmptyString(aid));
+        });
     }
 
     @Override
@@ -1445,24 +1399,23 @@
 
     @Override
     public void supplyIccPuk2ForApp(String puk, String newPin2, String aid, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PUK2, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " aid = " + aid);
-            }
-
-            try {
-                simProxy.supplyIccPuk2ForApp(rr.mSerial, RILUtils.convertNullToEmptyString(puk),
-                        RILUtils.convertNullToEmptyString(newPin2),
-                        RILUtils.convertNullToEmptyString(aid));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPuk2ForApp", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("supplyIccPuk2ForApp", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PUK2, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " aid = " + aid);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "supplyIccPuk2ForApp", () -> {
+            simProxy.supplyIccPuk2ForApp(rr.mSerial, RILUtils.convertNullToEmptyString(puk),
+                    RILUtils.convertNullToEmptyString(newPin2),
+                    RILUtils.convertNullToEmptyString(aid));
+        });
     }
 
     @Override
@@ -1472,25 +1425,24 @@
 
     @Override
     public void changeIccPinForApp(String oldPin, String newPin, String aid, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_SIM_PIN, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " oldPin = " + oldPin + " newPin = " + newPin + " aid = " + aid);
-            }
-
-            try {
-                simProxy.changeIccPinForApp(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(oldPin),
-                        RILUtils.convertNullToEmptyString(newPin),
-                        RILUtils.convertNullToEmptyString(aid));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "changeIccPinForApp", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("changeIccPinForApp", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_SIM_PIN, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " oldPin = " + oldPin + " newPin = " + newPin + " aid = " + aid);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "changeIccPinForApp", () -> {
+            simProxy.changeIccPinForApp(rr.mSerial,
+                    RILUtils.convertNullToEmptyString(oldPin),
+                    RILUtils.convertNullToEmptyString(newPin),
+                    RILUtils.convertNullToEmptyString(aid));
+        });
     }
 
     @Override
@@ -1500,102 +1452,92 @@
 
     @Override
     public void changeIccPin2ForApp(String oldPin2, String newPin2, String aid, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_SIM_PIN2, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " oldPin = " + oldPin2 + " newPin = " + newPin2 + " aid = " + aid);
-            }
-
-            try {
-                simProxy.changeIccPin2ForApp(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(oldPin2),
-                        RILUtils.convertNullToEmptyString(newPin2),
-                        RILUtils.convertNullToEmptyString(aid));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "changeIccPin2ForApp", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("changeIccPin2ForApp", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_SIM_PIN2, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " oldPin = " + oldPin2 + " newPin = " + newPin2 + " aid = " + aid);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "changeIccPin2ForApp", () -> {
+            simProxy.changeIccPin2ForApp(rr.mSerial,
+                    RILUtils.convertNullToEmptyString(oldPin2),
+                    RILUtils.convertNullToEmptyString(newPin2),
+                    RILUtils.convertNullToEmptyString(aid));
+        });
     }
 
     @Override
     public void supplyNetworkDepersonalization(String netpin, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " netpin = " + netpin);
-            }
-
-            try {
-                networkProxy.supplyNetworkDepersonalization(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(netpin));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(
-                        HAL_SERVICE_NETWORK, "supplyNetworkDepersonalization", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("supplyNetworkDepersonalization", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " netpin = " + netpin);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "supplyNetworkDepersonalization", () -> {
+            networkProxy.supplyNetworkDepersonalization(rr.mSerial,
+                    RILUtils.convertNullToEmptyString(netpin));
+        });
     }
 
     @Override
     public void supplySimDepersonalization(PersoSubState persoType, String controlKey,
             Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (simProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " controlKey = " + controlKey + " persoType" + persoType);
-            }
-
-            try {
-                simProxy.supplySimDepersonalization(rr.mSerial, persoType,
-                        RILUtils.convertNullToEmptyString(controlKey));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplySimDepersonalization", e);
-            }
-        } else {
-            if (PersoSubState.PERSOSUBSTATE_SIM_NETWORK == persoType) {
-                supplyNetworkDepersonalization(controlKey, result);
-                return;
-            }
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "supplySimDepersonalization: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        if (mHalVersion.get(HAL_SERVICE_SIM).less(RADIO_HAL_VERSION_1_5)
+                && PersoSubState.PERSOSUBSTATE_SIM_NETWORK == persoType) {
+            supplyNetworkDepersonalization(controlKey, result);
+            return;
         }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("supplySimDepersonalization", simProxy, result,
+                RADIO_HAL_VERSION_1_5)) {
+            return;
+        }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " controlKey = " + controlKey + " persoType" + persoType);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "supplySimDepersonalization", () -> {
+            simProxy.supplySimDepersonalization(rr.mSerial, persoType,
+                    RILUtils.convertNullToEmptyString(controlKey));
+        });
     }
 
     @Override
     public void getCurrentCalls(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_CURRENT_CALLS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.getCurrentCalls(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCurrentCalls", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("getCurrentCalls", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_CURRENT_CALLS, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "getCurrentCalls", () -> {
+            voiceProxy.getCurrentCalls(rr.mSerial);
+        });
     }
 
     @Override
@@ -1607,167 +1549,127 @@
 
     @Override
     public void enableModem(boolean enable, Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (modemProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_MODEM, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " enable = " + enable);
-            }
-
-            try {
-                modemProxy.enableModem(rr.mSerial, enable);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM,
-                        "enableModem", e);
-            }
-        } else {
-            if (RILJ_LOGV) riljLog("enableModem: not supported.");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("enableModem", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_MODEM, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " enable = " + enable);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "enableModem", () -> {
+            modemProxy.enableModem(rr.mSerial, enable);
+        });
     }
 
     @Override
     public void setSystemSelectionChannels(@NonNull List<RadioAccessSpecifier> specifiers,
             Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " setSystemSelectionChannels_1.3= " + specifiers);
-            }
-
-            try {
-                networkProxy.setSystemSelectionChannels(rr.mSerial, specifiers);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "setSystemSelectionChannels", e);
-            }
-        } else {
-            if (RILJ_LOGV) riljLog("setSystemSelectionChannels: not supported.");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setSystemSelectionChannels", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " setSystemSelectionChannels= " + specifiers);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setSystemSelectionChannels", () -> {
+            networkProxy.setSystemSelectionChannels(rr.mSerial, specifiers);
+        });
     }
 
     @Override
     public void getSystemSelectionChannels(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " getSystemSelectionChannels");
-            }
-
-            try {
-                networkProxy.getSystemSelectionChannels(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "getSystemSelectionChannels", e);
-            }
-        } else {
-            if (RILJ_LOGV) riljLog("getSystemSelectionChannels: not supported.");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getSystemSelectionChannels", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " getSystemSelectionChannels");
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getSystemSelectionChannels", () -> {
+            networkProxy.getSystemSelectionChannels(rr.mSerial);
+        });
     }
 
     @Override
     public void getModemStatus(Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (modemProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_MODEM_STATUS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                modemProxy.getModemStackStatus(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getModemStatus", e);
-            }
-        } else {
-            if (RILJ_LOGV) riljLog("getModemStatus: not supported.");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("getModemStatus", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_MODEM_STATUS, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "getModemStatus", () -> {
+            modemProxy.getModemStackStatus(rr.mSerial);
+        });
     }
 
     @Override
     public void dial(String address, boolean isEmergencyCall, EmergencyNumber emergencyNumberInfo,
             boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo, Message result) {
-        if (isEmergencyCall
-                && mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_1_4)
-                && emergencyNumberInfo != null) {
+        if (isEmergencyCall && emergencyNumberInfo != null) {
             emergencyDial(address, emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode,
                     uusInfo, result);
             return;
         }
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                // Do not log function arg for privacy
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.dial(rr.mSerial, address, clirMode, uusInfo);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "dial", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("dial", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "dial", () -> {
+            voiceProxy.dial(rr.mSerial, address, clirMode, uusInfo);
+        });
     }
 
     private void emergencyDial(String address, EmergencyNumber emergencyNumberInfo,
             boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (voiceProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_EMERGENCY_DIAL, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                // Do not log function arg for privacy
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.emergencyDial(rr.mSerial, RILUtils.convertNullToEmptyString(address),
-                        emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode, uusInfo);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "emergencyDial", e);
-            }
-        } else {
-            riljLoge("emergencyDial is not supported with 1.4 below IRadio");
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("emergencyDial", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_EMERGENCY_DIAL, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "emergencyDial", () -> {
+            voiceProxy.emergencyDial(rr.mSerial, RILUtils.convertNullToEmptyString(address),
+                    emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode, uusInfo);
+        });
     }
 
     @Override
@@ -1777,304 +1679,305 @@
 
     @Override
     public void getIMSIForApp(String aid, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_IMSI, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " aid = " + aid);
-            }
-            try {
-                simProxy.getImsiForApp(rr.mSerial, RILUtils.convertNullToEmptyString(aid));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getImsiForApp", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("getIMSIForApp", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_IMSI, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " aid = " + aid);
+        }
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "getIMSIForApp", () -> {
+            simProxy.getImsiForApp(rr.mSerial, RILUtils.convertNullToEmptyString(aid));
+        });
     }
 
     @Override
     public void hangupConnection(int gsmIndex, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " gsmIndex = " + gsmIndex);
-            }
-
-            try {
-                voiceProxy.hangup(rr.mSerial, gsmIndex);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "hangup", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("hangupConnection", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " gsmIndex = " + gsmIndex);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "hangupConnection", () -> {
+            voiceProxy.hangup(rr.mSerial, gsmIndex);
+        });
     }
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @Override
     public void hangupWaitingOrBackground(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.hangupWaitingOrBackground(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "hangupWaitingOrBackground", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("hangupWaitingOrBackground", voiceProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "hangupWaitingOrBackground", () -> {
+            voiceProxy.hangupWaitingOrBackground(rr.mSerial);
+        });
     }
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @Override
     public void hangupForegroundResumeBackground(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.hangupForegroundResumeBackground(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(
-                        HAL_SERVICE_VOICE, "hangupForegroundResumeBackground", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("hangupForegroundResumeBackground", voiceProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "hangupForegroundResumeBackground", () -> {
+            voiceProxy.hangupForegroundResumeBackground(rr.mSerial);
+        });
     }
 
     @Override
     public void switchWaitingOrHoldingAndActive(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.switchWaitingOrHoldingAndActive(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE,
-                        "switchWaitingOrHoldingAndActive", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("switchWaitingOrHoldingAndActive", voiceProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "switchWaitingOrHoldingAndActive", () -> {
+            voiceProxy.switchWaitingOrHoldingAndActive(rr.mSerial);
+        });
     }
 
     @Override
     public void conference(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CONFERENCE, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.conference(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "conference", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("conference", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CONFERENCE, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "conference", () -> {
+            voiceProxy.conference(rr.mSerial);
+        });
     }
 
     @Override
     public void rejectCall(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_UDUB, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.rejectCall(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "rejectCall", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("rejectCall", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_UDUB, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "rejectCall", () -> {
+            voiceProxy.rejectCall(rr.mSerial);
+        });
     }
 
     @Override
     public void getLastCallFailCause(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.getLastCallFailCause(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getLastCallFailCause", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("getLastCallFailCause", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "getLastCallFailCause", () -> {
+            voiceProxy.getLastCallFailCause(rr.mSerial);
+        });
     }
 
     @Override
     public void getSignalStrength(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SIGNAL_STRENGTH, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getSignalStrength(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getSignalStrength", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getSignalStrength", networkProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SIGNAL_STRENGTH, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getSignalStrength", () -> {
+            networkProxy.getSignalStrength(rr.mSerial);
+        });
     }
 
     @Override
     public void getVoiceRegistrationState(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_VOICE_REGISTRATION_STATE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_VOICE_REGISTRATION_STATE);
-            if (RILJ_LOGD) {
-                riljLog("getVoiceRegistrationState: overrideHalVersion=" + overrideHalVersion);
-            }
-
-            try {
-                networkProxy.getVoiceRegistrationState(rr.mSerial, overrideHalVersion);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getVoiceRegistrationState", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getVoiceRegistrationState", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_VOICE_REGISTRATION_STATE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_VOICE_REGISTRATION_STATE);
+        if (RILJ_LOGD) {
+            riljLog("getVoiceRegistrationState: overrideHalVersion=" + overrideHalVersion);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getVoiceRegistrationState", () -> {
+            networkProxy.getVoiceRegistrationState(rr.mSerial, overrideHalVersion);
+        });
     }
 
     @Override
     public void getDataRegistrationState(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_DATA_REGISTRATION_STATE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_DATA_REGISTRATION_STATE);
-            if (RILJ_LOGD) {
-                riljLog("getDataRegistrationState: overrideHalVersion=" + overrideHalVersion);
-            }
-
-            try {
-                networkProxy.getDataRegistrationState(rr.mSerial, overrideHalVersion);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getDataRegistrationState", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getDataRegistrationState", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_DATA_REGISTRATION_STATE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_DATA_REGISTRATION_STATE);
+        if (RILJ_LOGD) {
+            riljLog("getDataRegistrationState: overrideHalVersion=" + overrideHalVersion);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getDataRegistrationState", () -> {
+            networkProxy.getDataRegistrationState(rr.mSerial, overrideHalVersion);
+        });
     }
 
     @Override
     public void getOperator(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_OPERATOR, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getOperator(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getOperator", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getOperator", networkProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_OPERATOR, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getOperator", () -> {
+            networkProxy.getOperator(rr.mSerial);
+        });
     }
 
     @UnsupportedAppUsage
     @Override
     public void setRadioPower(boolean on, boolean forEmergencyCall,
             boolean preferredForEmergencyCall, Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_RADIO_POWER, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " on = " + on + " forEmergencyCall= " + forEmergencyCall
-                        + " preferredForEmergencyCall="  + preferredForEmergencyCall);
-            }
-
-            try {
-                modemProxy.setRadioPower(rr.mSerial, on, forEmergencyCall,
-                        preferredForEmergencyCall);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "setRadioPower", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("setRadioPower", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_RADIO_POWER, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " on = " + on + " forEmergencyCall= " + forEmergencyCall
+                    + " preferredForEmergencyCall=" + preferredForEmergencyCall);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "setRadioPower", () -> {
+            modemProxy.setRadioPower(rr.mSerial, on, forEmergencyCall,
+                    preferredForEmergencyCall);
+        });
     }
 
     @Override
     public void sendDtmf(char c, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_DTMF, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                // Do not log function arg for privacy
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.sendDtmf(rr.mSerial, c + "");
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendDtmf", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("sendDtmf", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_DTMF, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "sendDtmf", () -> {
+            voiceProxy.sendDtmf(rr.mSerial, c + "");
+        });
     }
 
     @Override
     public void sendSMS(String smscPdu, String pdu, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SEND_SMS, result, mRILDefaultWorkSource);
-
-            // Do not log function args for privacy
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                messagingProxy.sendSms(rr.mSerial, smscPdu, pdu);
-                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
-                        SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendSMS", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("sendSMS", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SEND_SMS, result, mRILDefaultWorkSource);
+
+        // Do not log function args for privacy
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "sendSMS", () -> {
+            messagingProxy.sendSms(rr.mSerial, smscPdu, pdu);
+            mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
+                    SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
+        });
     }
 
     /**
@@ -2097,69 +2000,52 @@
 
     @Override
     public void sendSMSExpectMore(String smscPdu, String pdu, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SEND_SMS_EXPECT_MORE, result,
-                    mRILDefaultWorkSource);
-
-            // Do not log function arg for privacy
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                messagingProxy.sendSmsExpectMore(rr.mSerial, smscPdu, pdu);
-                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
-                        SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendSMSExpectMore", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("sendSMSExpectMore", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SEND_SMS_EXPECT_MORE, result,
+                mRILDefaultWorkSource);
+
+        // Do not log function arg for privacy
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "sendSMSExpectMore", () -> {
+            messagingProxy.sendSmsExpectMore(rr.mSerial, smscPdu, pdu);
+            mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
+                    SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
+        });
     }
 
     @Override
-    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
-            boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
-            boolean matchAllRuleAllowed, Message result) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (!dataProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SETUP_DATA_CALL, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + ",reason=" + RILUtils.setupDataReasonToString(reason)
-                        + ",accessNetworkType=" + AccessNetworkType.toString(accessNetworkType)
-                        + ",dataProfile=" + dataProfile + ",isRoaming=" + isRoaming
-                        + ",allowRoaming=" + allowRoaming
-                        + ",linkProperties=" + linkProperties + ",pduSessionId=" + pduSessionId
-                        + ",sliceInfo=" + sliceInfo + ",trafficDescriptor=" + trafficDescriptor
-                        + ",matchAllRuleAllowed=" + matchAllRuleAllowed);
-            }
-
-            try {
-                dataProxy.setupDataCall(rr.mSerial, mPhoneId, accessNetworkType, dataProfile,
-                        isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
-                        trafficDescriptor, matchAllRuleAllowed);
-            } catch (RemoteException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setupDataCall", e);
-            } catch (RuntimeException e) {
-                riljLoge("setupDataCall RuntimeException: " + e);
-                int error = RadioError.SYSTEM_ERR;
-                int responseType = RadioResponseType.SOLICITED;
-                processResponseInternal(HAL_SERVICE_DATA, rr.mSerial, error, responseType);
-                processResponseDoneInternal(rr, error, responseType, null);
-            }
-        } else {
-            riljLoge("setupDataCall: DataProxy is empty");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
-                result.sendToTarget();
-            }
+    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean allowRoaming,
+            int reason, LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo,
+            TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, Message result) {
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("setupDataCall", dataProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SETUP_DATA_CALL, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + ",reason=" + RILUtils.setupDataReasonToString(reason)
+                    + ",accessNetworkType=" + AccessNetworkType.toString(accessNetworkType)
+                    + ",dataProfile=" + dataProfile + ",allowRoaming=" + allowRoaming
+                    + ",linkProperties=" + linkProperties + ",pduSessionId=" + pduSessionId
+                    + ",sliceInfo=" + sliceInfo + ",trafficDescriptor=" + trafficDescriptor
+                    + ",matchAllRuleAllowed=" + matchAllRuleAllowed);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "setupDataCall", () -> {
+            dataProxy.setupDataCall(rr.mSerial, accessNetworkType, dataProfile, allowRoaming,
+                    reason, linkProperties, pduSessionId, sliceInfo, trafficDescriptor,
+                    matchAllRuleAllowed);
+        });
     }
 
     @Override
@@ -2171,257 +2057,251 @@
     @Override
     public void iccIOForApp(int command, int fileId, String path, int p1, int p2, int p3,
             String data, String pin2, String aid, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_IO, result, mRILDefaultWorkSource);
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("iccIOForApp", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
+        }
 
-            if (RILJ_LOGD) {
-                if (TelephonyUtils.IS_DEBUGGABLE) {
-                    riljLog(rr.serialString() + "> iccIO: " + RILUtils.requestToString(rr.mRequest)
-                            + " command = 0x" + Integer.toHexString(command) + " fileId = 0x"
-                            + Integer.toHexString(fileId) + " path = " + path + " p1 = " + p1
-                            + " p2 = " + p2 + " p3 = " + " data = " + data + " aid = " + aid);
-                } else {
-                    riljLog(rr.serialString() + "> iccIO: "
-                            + RILUtils.requestToString(rr.mRequest));
-                }
-            }
+        RILRequest rr = obtainRequest(RIL_REQUEST_SIM_IO, result, mRILDefaultWorkSource);
 
-            try {
-                simProxy.iccIoForApp(rr.mSerial, command, fileId,
-                        RILUtils.convertNullToEmptyString(path), p1, p2, p3,
-                        RILUtils.convertNullToEmptyString(data),
-                        RILUtils.convertNullToEmptyString(pin2),
-                        RILUtils.convertNullToEmptyString(aid));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccIoForApp", e);
+        if (RILJ_LOGD) {
+            if (TelephonyUtils.IS_DEBUGGABLE) {
+                riljLog(rr.serialString() + "> iccIO: " + RILUtils.requestToString(rr.mRequest)
+                        + " command = 0x" + Integer.toHexString(command) + " fileId = 0x"
+                        + Integer.toHexString(fileId) + " path = " + path + " p1 = " + p1
+                        + " p2 = " + p2 + " p3 = " + " data = " + data + " aid = " + aid);
+            } else {
+                riljLog(rr.serialString() + "> iccIO: "
+                        + RILUtils.requestToString(rr.mRequest));
             }
         }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "iccIOForApp", () -> {
+            simProxy.iccIoForApp(rr.mSerial, command, fileId,
+                    RILUtils.convertNullToEmptyString(path), p1, p2, p3,
+                    RILUtils.convertNullToEmptyString(data),
+                    RILUtils.convertNullToEmptyString(pin2),
+                    RILUtils.convertNullToEmptyString(aid));
+        });
     }
 
     @Override
     public void sendUSSD(String ussd, Message result) {
-        RadioVoiceProxy voiceProxy =
-                getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SEND_USSD, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                String logUssd = "*******";
-                if (RILJ_LOGV) logUssd = ussd;
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " ussd = " + logUssd);
-            }
-
-            try {
-                voiceProxy.sendUssd(rr.mSerial, RILUtils.convertNullToEmptyString(ussd));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendUssd", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("sendUSSD", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SEND_USSD, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            String logUssd = "*******";
+            if (RILJ_LOGV) logUssd = ussd;
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " ussd = " + logUssd);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "sendUSSD", () -> {
+            voiceProxy.sendUssd(rr.mSerial, RILUtils.convertNullToEmptyString(ussd));
+        });
     }
 
     @Override
     public void cancelPendingUssd(Message result) {
-        RadioVoiceProxy voiceProxy =
-                getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_USSD, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.cancelPendingUssd(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "cancelPendingUssd", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("cancelPendingUssd", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_USSD, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "cancelPendingUssd", () -> {
+            voiceProxy.cancelPendingUssd(rr.mSerial);
+        });
     }
 
     @Override
     public void getCLIR(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_CLIR, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.getClir(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getClir", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("getCLIR", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_CLIR, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "getCLIR", () -> {
+            voiceProxy.getClir(rr.mSerial);
+        });
     }
 
     @Override
     public void setCLIR(int clirMode, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_CLIR, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " clirMode = " + clirMode);
-            }
-
-            try {
-                voiceProxy.setClir(rr.mSerial, clirMode);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setClir", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("setCLIR", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_CLIR, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " clirMode = " + clirMode);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "setCLIR", () -> {
+            voiceProxy.setClir(rr.mSerial, clirMode);
+        });
     }
 
     @Override
     public void queryCallForwardStatus(int cfReason, int serviceClass, String number,
             Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " cfreason = " + cfReason + " serviceClass = " + serviceClass);
-            }
-
-            try {
-                voiceProxy.getCallForwardStatus(rr.mSerial, cfReason, serviceClass, number);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCallForwardStatus", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("queryCallForwardStatus", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " cfReason = " + cfReason + " serviceClass = " + serviceClass);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "queryCallForwardStatus", () -> {
+            voiceProxy.getCallForwardStatus(rr.mSerial, cfReason, serviceClass, number);
+        });
     }
 
     @Override
     public void setCallForward(int action, int cfReason, int serviceClass, String number,
             int timeSeconds, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_CALL_FORWARD, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " action = " + action + " cfReason = " + cfReason + " serviceClass = "
-                        + serviceClass + " timeSeconds = " + timeSeconds);
-            }
-
-            try {
-                voiceProxy.setCallForward(
-                        rr.mSerial, action, cfReason, serviceClass, number, timeSeconds);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setCallForward", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("setCallForward", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_CALL_FORWARD, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " action = " + action + " cfReason = " + cfReason + " serviceClass = "
+                    + serviceClass + " timeSeconds = " + timeSeconds);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "setCallForward", () -> {
+            voiceProxy.setCallForward(
+                    rr.mSerial, action, cfReason, serviceClass, number, timeSeconds);
+        });
     }
 
     @Override
     public void queryCallWaiting(int serviceClass, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CALL_WAITING, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " serviceClass = " + serviceClass);
-            }
-
-            try {
-                voiceProxy.getCallWaiting(rr.mSerial, serviceClass);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCallWaiting", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("queryCallWaiting", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CALL_WAITING, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " serviceClass = " + serviceClass);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "queryCallWaiting", () -> {
+            voiceProxy.getCallWaiting(rr.mSerial, serviceClass);
+        });
     }
 
     @Override
     public void setCallWaiting(boolean enable, int serviceClass, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_CALL_WAITING, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " enable = " + enable + " serviceClass = " + serviceClass);
-            }
-
-            try {
-                voiceProxy.setCallWaiting(rr.mSerial, enable, serviceClass);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setCallWaiting", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("setCallWaiting", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_CALL_WAITING, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " enable = " + enable + " serviceClass = " + serviceClass);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "setCallWaiting", () -> {
+            voiceProxy.setCallWaiting(rr.mSerial, enable, serviceClass);
+        });
     }
 
     @Override
     public void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SMS_ACKNOWLEDGE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " success = " + success + " cause = " + cause);
-            }
-
-            try {
-                messagingProxy.acknowledgeLastIncomingGsmSms(rr.mSerial, success, cause);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
-                        "acknowledgeLastIncomingGsmSms", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("acknowledgeLastIncomingGsmSms", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SMS_ACKNOWLEDGE, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " success = " + success + " cause = " + cause);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "acknowledgeLastIncomingGsmSms", () -> {
+            messagingProxy.acknowledgeLastIncomingGsmSms(rr.mSerial, success, cause);
+        });
     }
 
     @Override
     public void acceptCall(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ANSWER, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.acceptCall(rr.mSerial);
-                mMetrics.writeRilAnswer(mPhoneId, rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "acceptCall", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("acceptCall", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ANSWER, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "acceptCall", () -> {
+            voiceProxy.acceptCall(rr.mSerial);
+            mMetrics.writeRilAnswer(mPhoneId, rr.mSerial);
+        });
     }
 
     @Override
     public void deactivateDataCall(int cid, int reason, Message result) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (!dataProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_DEACTIVATE_DATA_CALL, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " cid = " + cid + " reason = "
-                        + RILUtils.deactivateDataReasonToString(reason));
-            }
-
-            try {
-                dataProxy.deactivateDataCall(rr.mSerial, cid, reason);
-                mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial, cid, reason);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "deactivateDataCall", e);
-            }
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("deactivateDataCall", dataProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_DEACTIVATE_DATA_CALL, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " cid = " + cid + " reason = "
+                    + RILUtils.deactivateDataReasonToString(reason));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "deactivateDataCall", () -> {
+            dataProxy.deactivateDataCall(rr.mSerial, cid, reason);
+            mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial, cid, reason);
+        });
     }
 
     @Override
@@ -2433,26 +2313,27 @@
     @Override
     public void queryFacilityLockForApp(String facility, String password, int serviceClass,
             String appId, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_FACILITY_LOCK, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " facility = " + facility + " serviceClass = " + serviceClass
-                        + " appId = " + appId);
-            }
-
-            try {
-                simProxy.getFacilityLockForApp(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(facility),
-                        RILUtils.convertNullToEmptyString(password),
-                        serviceClass, RILUtils.convertNullToEmptyString(appId));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getFacilityLockForApp", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("queryFacilityLockForApp", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_FACILITY_LOCK, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " facility = " + facility + " serviceClass = " + serviceClass
+                    + " appId = " + appId);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "queryFacilityLockForApp", () -> {
+            simProxy.getFacilityLockForApp(rr.mSerial,
+                    RILUtils.convertNullToEmptyString(facility),
+                    RILUtils.convertNullToEmptyString(password), serviceClass,
+                    RILUtils.convertNullToEmptyString(appId));
+        });
+
     }
 
     @Override
@@ -2464,131 +2345,132 @@
     @Override
     public void setFacilityLockForApp(String facility, boolean lockState, String password,
             int serviceClass, String appId, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_FACILITY_LOCK, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " facility = " + facility + " lockstate = " + lockState
-                        + " serviceClass = " + serviceClass + " appId = " + appId);
-            }
-
-            try {
-                simProxy.setFacilityLockForApp(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(facility), lockState,
-                        RILUtils.convertNullToEmptyString(password), serviceClass,
-                        RILUtils.convertNullToEmptyString(appId));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setFacilityLockForApp", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("setFacilityLockForApp", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_FACILITY_LOCK, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " facility = " + facility + " lockstate = " + lockState
+                    + " serviceClass = " + serviceClass + " appId = " + appId);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "setFacilityLockForApp", () -> {
+            simProxy.setFacilityLockForApp(rr.mSerial,
+                    RILUtils.convertNullToEmptyString(facility), lockState,
+                    RILUtils.convertNullToEmptyString(password), serviceClass,
+                    RILUtils.convertNullToEmptyString(appId));
+        });
     }
 
     @Override
     public void changeBarringPassword(String facility, String oldPwd, String newPwd,
             Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_BARRING_PASSWORD, result,
-                    mRILDefaultWorkSource);
-
-            // Do not log all function args for privacy
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + "facility = " + facility);
-            }
-
-            try {
-                networkProxy.setBarringPassword(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(facility),
-                        RILUtils.convertNullToEmptyString(oldPwd),
-                        RILUtils.convertNullToEmptyString(newPwd));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "changeBarringPassword", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("changeBarringPassword", networkProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_BARRING_PASSWORD, result,
+                mRILDefaultWorkSource);
+
+        // Do not log all function args for privacy
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + "facility = " + facility);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "changeBarringPassword", () -> {
+            networkProxy.setBarringPassword(rr.mSerial,
+                    RILUtils.convertNullToEmptyString(facility),
+                    RILUtils.convertNullToEmptyString(oldPwd),
+                    RILUtils.convertNullToEmptyString(newPwd));
+        });
     }
 
     @Override
     public void getNetworkSelectionMode(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getNetworkSelectionMode(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getNetworkSelectionMode", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getNetworkSelectionMode", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getNetworkSelectionMode", () -> {
+            networkProxy.getNetworkSelectionMode(rr.mSerial);
+        });
     }
 
     @Override
     public void setNetworkSelectionModeAutomatic(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.setNetworkSelectionModeAutomatic(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(
-                        HAL_SERVICE_NETWORK, "setNetworkSelectionModeAutomatic", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setNetworkSelectionModeAutomatic", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setNetworkSelectionModeAutomatic",
+                () -> {
+                    networkProxy.setNetworkSelectionModeAutomatic(rr.mSerial);
+                });
     }
 
     @Override
     public void setNetworkSelectionModeManual(String operatorNumeric, int ran, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " operatorNumeric = " + operatorNumeric + ", ran = " + ran);
-            }
-
-            try {
-                networkProxy.setNetworkSelectionModeManual(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(operatorNumeric), ran);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "setNetworkSelectionModeManual", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setNetworkSelectionModeManual", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " operatorNumeric = " + operatorNumeric + ", ran = " + ran);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setNetworkSelectionModeManual", () -> {
+            networkProxy.setNetworkSelectionModeManual(rr.mSerial,
+                    RILUtils.convertNullToEmptyString(operatorNumeric), ran);
+        });
     }
 
     @Override
     public void getAvailableNetworks(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getAvailableNetworks(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getAvailableNetworks", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getAvailableNetworks", networkProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getAvailableNetworks", () -> {
+            networkProxy.getAvailableNetworks(rr.mSerial);
+        });
     }
 
     /**
@@ -2599,526 +2481,486 @@
      */
     @Override
     public void startNetworkScan(NetworkScanRequest networkScanRequest, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
-            HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_START_NETWORK_SCAN);
-            if (RILJ_LOGD) {
-                riljLog("startNetworkScan: overrideHalVersion=" + overrideHalVersion);
-            }
-
-            RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
-                    mRILDefaultWorkSource, networkScanRequest);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.startNetworkScan(rr.mSerial, networkScanRequest, overrideHalVersion,
-                        result);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "startNetworkScan", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startNetworkScan: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("startNetworkScan", networkProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_START_NETWORK_SCAN);
+        if (RILJ_LOGD) {
+            riljLog("startNetworkScan: overrideHalVersion=" + overrideHalVersion);
+        }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
+                mRILDefaultWorkSource, networkScanRequest);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "startNetworkScan", () -> {
+            networkProxy.startNetworkScan(rr.mSerial, networkScanRequest, overrideHalVersion,
+                    result);
+        });
     }
 
     @Override
     public void stopNetworkScan(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_STOP_NETWORK_SCAN, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.stopNetworkScan(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "stopNetworkScan", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopNetworkScan: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("stopNetworkScan", networkProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_STOP_NETWORK_SCAN, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "stopNetworkScan", () -> {
+            networkProxy.stopNetworkScan(rr.mSerial);
+        });
     }
 
     @Override
     public void startDtmf(char c, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_DTMF_START, result, mRILDefaultWorkSource);
-
-            // Do not log function arg for privacy
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.startDtmf(rr.mSerial, c + "");
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "startDtmf", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("startDtmf", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_DTMF_START, result, mRILDefaultWorkSource);
+
+        // Do not log function arg for privacy
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "startDtmf", () -> {
+            voiceProxy.startDtmf(rr.mSerial, c + "");
+        });
     }
 
     @Override
     public void stopDtmf(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_DTMF_STOP, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.stopDtmf(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "stopDtmf", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("stopDtmf", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_DTMF_STOP, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "stopDtmf", () -> {
+            voiceProxy.stopDtmf(rr.mSerial);
+        });
     }
 
     @Override
     public void separateConnection(int gsmIndex, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SEPARATE_CONNECTION, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " gsmIndex = " + gsmIndex);
-            }
-
-            try {
-                voiceProxy.separateConnection(rr.mSerial, gsmIndex);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "separateConnection", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("separateConnection", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SEPARATE_CONNECTION, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " gsmIndex = " + gsmIndex);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "separateConnection", () -> {
+            voiceProxy.separateConnection(rr.mSerial, gsmIndex);
+        });
     }
 
     @Override
     public void getBasebandVersion(Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_BASEBAND_VERSION, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                modemProxy.getBasebandVersion(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getBasebandVersion", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("getBasebandVersion", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_BASEBAND_VERSION, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "getBasebandVersion", () -> {
+            modemProxy.getBasebandVersion(rr.mSerial);
+        });
     }
 
     @Override
     public void setMute(boolean enableMute, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_MUTE, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " enableMute = " + enableMute);
-            }
-
-            try {
-                voiceProxy.setMute(rr.mSerial, enableMute);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setMute", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("setMute", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_MUTE, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " enableMute = " + enableMute);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "setMute", () -> {
+            voiceProxy.setMute(rr.mSerial, enableMute);
+        });
     }
 
     @Override
     public void getMute(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_MUTE, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.getMute(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getMute", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("getMute", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_MUTE, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "getMute", () -> {
+            voiceProxy.getMute(rr.mSerial);
+        });
     }
 
     @Override
     public void queryCLIP(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CLIP, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.getClip(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getClip", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("queryCLIP", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
-    }
 
-    /**
-     * @deprecated
-     */
-    @Override
-    @Deprecated
-    public void getPDPContextList(Message result) {
-        getDataCallList(result);
+        RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CLIP, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "queryCLIP", () -> {
+            voiceProxy.getClip(rr.mSerial);
+        });
     }
 
     @Override
     public void getDataCallList(Message result) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (!dataProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_DATA_CALL_LIST, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                dataProxy.getDataCallList(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "getDataCallList", e);
-            }
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("getDataCallList", dataProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
-    }
 
-    // TODO(b/171260715) Remove when HAL definition is removed
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @Override
-    public void invokeOemRilRequestRaw(byte[] data, Message response) {
-    }
+        RILRequest rr = obtainRequest(RIL_REQUEST_DATA_CALL_LIST, result, mRILDefaultWorkSource);
 
-    // TODO(b/171260715) Remove when HAL definition is removed
-    @Override
-    public void invokeOemRilRequestStrings(String[] strings, Message result) {
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "getDataCallList", () -> {
+            dataProxy.getDataCallList(rr.mSerial);
+        });
     }
 
     @Override
     public void setSuppServiceNotifications(boolean enable, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " enable = " + enable);
-            }
-
-            try {
-                networkProxy.setSuppServiceNotifications(rr.mSerial, enable);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "setSuppServiceNotifications", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setSuppServiceNotifications", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " enable = " + enable);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setSuppServiceNotifications", () -> {
+            networkProxy.setSuppServiceNotifications(rr.mSerial, enable);
+        });
     }
 
     @Override
     public void writeSmsToSim(int status, String smsc, String pdu, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_WRITE_SMS_TO_SIM, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGV) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " " + status);
-            }
-
-            try {
-                messagingProxy.writeSmsToSim(rr.mSerial, status,
-                        RILUtils.convertNullToEmptyString(smsc),
-                        RILUtils.convertNullToEmptyString(pdu));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "writeSmsToSim", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("writeSmsToSim", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_WRITE_SMS_TO_SIM, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGV) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " " + status);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "writeSmsToSim", () -> {
+            messagingProxy.writeSmsToSim(rr.mSerial, status,
+                    RILUtils.convertNullToEmptyString(smsc),
+                    RILUtils.convertNullToEmptyString(pdu));
+        });
     }
 
     @Override
     public void deleteSmsOnSim(int index, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_DELETE_SMS_ON_SIM, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGV) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " index = " + index);
-            }
-
-            try {
-                messagingProxy.deleteSmsOnSim(rr.mSerial, index);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "deleteSmsOnSim", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("deleteSmsOnSim", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_DELETE_SMS_ON_SIM, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGV) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " index = " + index);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "deleteSmsOnSim", () -> {
+            messagingProxy.deleteSmsOnSim(rr.mSerial, index);
+        });
     }
 
     @Override
     public void setBandMode(int bandMode, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_BAND_MODE, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " bandMode = " + bandMode);
-            }
-
-            try {
-                networkProxy.setBandMode(rr.mSerial, bandMode);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setBandMode", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setBandMode", networkProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_BAND_MODE, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " bandMode = " + bandMode);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setBandMode", () -> {
+            networkProxy.setBandMode(rr.mSerial, bandMode);
+        });
     }
 
     @Override
     public void queryAvailableBandMode(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getAvailableBandModes(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "queryAvailableBandMode", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("queryAvailableBandMode", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "queryAvailableBandMode", () -> {
+            networkProxy.getAvailableBandModes(rr.mSerial);
+        });
     }
 
     @Override
     public void sendEnvelope(String contents, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " contents = " + contents);
-            }
-
-            try {
-                simProxy.sendEnvelope(rr.mSerial, RILUtils.convertNullToEmptyString(contents));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendEnvelope", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("sendEnvelope", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " contents = " + contents);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "sendEnvelope", () -> {
+            simProxy.sendEnvelope(rr.mSerial, RILUtils.convertNullToEmptyString(contents));
+        });
     }
 
     @Override
     public void sendTerminalResponse(String contents, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " contents = " + (TelephonyUtils.IS_DEBUGGABLE
-                        ? contents : RILUtils.convertToCensoredTerminalResponse(contents)));
-            }
-
-            try {
-                simProxy.sendTerminalResponseToSim(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(contents));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendTerminalResponse", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("sendTerminalResponse", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " contents = " + (TelephonyUtils.IS_DEBUGGABLE
+                    ? contents : RILUtils.convertToCensoredTerminalResponse(contents)));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "sendTerminalResponse", () -> {
+            simProxy.sendTerminalResponseToSim(rr.mSerial,
+                    RILUtils.convertNullToEmptyString(contents));
+        });
     }
 
     @Override
     public void sendEnvelopeWithStatus(String contents, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " contents = " + contents);
-            }
-
-            try {
-                simProxy.sendEnvelopeWithStatus(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(contents));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendEnvelopeWithStatus", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("sendEnvelopeWithStatus", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " contents = " + contents);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "sendEnvelopeWithStatus", () -> {
+            simProxy.sendEnvelopeWithStatus(rr.mSerial,
+                    RILUtils.convertNullToEmptyString(contents));
+        });
     }
 
     @Override
     public void explicitCallTransfer(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_EXPLICIT_CALL_TRANSFER, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.explicitCallTransfer(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "explicitCallTransfer", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("explicitCallTransfer", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_EXPLICIT_CALL_TRANSFER, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "explicitCallTransfer", () -> {
+            voiceProxy.explicitCallTransfer(rr.mSerial);
+        });
     }
 
     @Override
     public void setPreferredNetworkType(@PrefNetworkMode int networkType , Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " networkType = " + networkType);
-            }
-            mAllowedNetworkTypesBitmask = RadioAccessFamily.getRafFromNetworkType(networkType);
-            mMetrics.writeSetPreferredNetworkType(mPhoneId, networkType);
-
-            try {
-                networkProxy.setPreferredNetworkTypeBitmap(rr.mSerial, mAllowedNetworkTypesBitmask);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setPreferredNetworkType", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setPreferredNetworkType", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " networkType = " + networkType);
+        }
+        mAllowedNetworkTypesBitmask = RadioAccessFamily.getRafFromNetworkType(networkType);
+        mMetrics.writeSetPreferredNetworkType(mPhoneId, networkType);
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setPreferredNetworkType", () -> {
+            networkProxy.setPreferredNetworkTypeBitmap(rr.mSerial, mAllowedNetworkTypesBitmask);
+        });
     }
 
     @Override
     public void getPreferredNetworkType(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getAllowedNetworkTypesBitmap(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getPreferredNetworkType", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getPreferredNetworkType", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getPreferredNetworkType", () -> {
+            networkProxy.getAllowedNetworkTypesBitmap(rr.mSerial);
+        });
     }
 
     @Override
     public void setAllowedNetworkTypesBitmap(
             @TelephonyManager.NetworkTypeBitMask int networkTypeBitmask, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            if (mHalVersion.get(HAL_SERVICE_NETWORK).less(RADIO_HAL_VERSION_1_6)) {
-                // For older HAL, redirects the call to setPreferredNetworkType.
-                setPreferredNetworkType(
-                        RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmask), result);
-                return;
-            }
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-            mAllowedNetworkTypesBitmask = networkTypeBitmask;
-
-            try {
-                networkProxy.setAllowedNetworkTypesBitmap(rr.mSerial, mAllowedNetworkTypesBitmask);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "setAllowedNetworkTypeBitmask", e);
-            }
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).less(RADIO_HAL_VERSION_1_6)) {
+            // For older HAL, redirects the call to setPreferredNetworkType.
+            setPreferredNetworkType(
+                    RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmask), result);
+            return;
         }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setAllowedNetworkTypesBitmap", networkProxy, result,
+                RADIO_HAL_VERSION_1_6)) {
+            return;
+        }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+        mAllowedNetworkTypesBitmask = networkTypeBitmask;
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setAllowedNetworkTypesBitmap", () -> {
+            networkProxy.setAllowedNetworkTypesBitmap(rr.mSerial, mAllowedNetworkTypesBitmask);
+        });
     }
 
     @Override
     public void getAllowedNetworkTypesBitmap(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getAllowedNetworkTypesBitmap(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "getAllowedNetworkTypeBitmask", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getAllowedNetworkTypesBitmap", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getAllowedNetworkTypesBitmap", () -> {
+            networkProxy.getAllowedNetworkTypesBitmap(rr.mSerial);
+        });
     }
 
     @Override
     public void setLocationUpdates(boolean enable, WorkSource workSource, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOCATION_UPDATES, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " enable = " + enable);
-            }
-
-            try {
-                networkProxy.setLocationUpdates(rr.mSerial, enable);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setLocationUpdates", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setLocationUpdates", networkProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOCATION_UPDATES, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " enable = " + enable);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setLocationUpdates", () -> {
+            networkProxy.setLocationUpdates(rr.mSerial, enable);
+        });
     }
 
     /**
@@ -3126,32 +2968,22 @@
      */
     @Override
     public void isNrDualConnectivityEnabled(Message result, WorkSource workSource) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.isNrDualConnectivityEnabled(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "isNrDualConnectivityEnabled", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "isNrDualConnectivityEnabled: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("isNrDualConnectivityEnabled", networkProxy, result,
+                RADIO_HAL_VERSION_1_6)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "isNrDualConnectivityEnabled", () -> {
+            networkProxy.isNrDualConnectivityEnabled(rr.mSerial);
+        });
     }
 
     /**
@@ -3168,30 +3000,23 @@
     @Override
     public void setNrDualConnectivityState(int nrDualConnectivityState, Message result,
             WorkSource workSource) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " enable = " + nrDualConnectivityState);
-            }
-
-            try {
-                networkProxy.setNrDualConnectivityState(rr.mSerial, (byte) nrDualConnectivityState);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "enableNrDualConnectivity", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "enableNrDualConnectivity: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setNrDualConnectivityState", networkProxy, result,
+                RADIO_HAL_VERSION_1_6)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " enable = " + nrDualConnectivityState);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setNrDualConnectivityState", () -> {
+            networkProxy.setNrDualConnectivityState(rr.mSerial, (byte) nrDualConnectivityState);
+        });
     }
 
     private void setVoNrEnabled(boolean enabled) {
@@ -3207,30 +3032,27 @@
      */
     @Override
     public void isVoNrEnabled(Message result, WorkSource workSource) {
-
-        if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-            if (!voiceProxy.isEmpty()) {
-                RILRequest rr = obtainRequest(RIL_REQUEST_IS_VONR_ENABLED , result,
-                        getDefaultWorkSourceIfInvalid(workSource));
-
-                if (RILJ_LOGD) {
-                    riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-                }
-
-                try {
-                    voiceProxy.isVoNrEnabled(rr.mSerial);
-                } catch (RemoteException | RuntimeException e) {
-                    handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "isVoNrEnabled", e);
-                }
-            }
-        } else {
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        // Send null result so errors aren't sent in canMakeRequest
+        if (!canMakeRequest("isVoNrEnabled", voiceProxy, null, RADIO_HAL_VERSION_2_0)) {
             boolean isEnabled = isVoNrEnabled();
             if (result != null) {
                 AsyncResult.forMessage(result, isEnabled, null);
                 result.sendToTarget();
             }
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_IS_VONR_ENABLED, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "isVoNrEnabled", () -> {
+            voiceProxy.isVoNrEnabled(rr.mSerial);
+        });
     }
 
     /**
@@ -3240,24 +3062,9 @@
     @Override
     public void setVoNrEnabled(boolean enabled, Message result, WorkSource workSource) {
         setVoNrEnabled(enabled);
-
-        if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-            if (!voiceProxy.isEmpty()) {
-                RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_VONR, result,
-                        getDefaultWorkSourceIfInvalid(workSource));
-
-                if (RILJ_LOGD) {
-                    riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-                }
-
-                try {
-                    voiceProxy.setVoNrEnabled(rr.mSerial, enabled);
-                } catch (RemoteException | RuntimeException e) {
-                    handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setVoNrEnabled", e);
-                }
-            }
-        } else {
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        // Send null result so errors aren't sent in canMakeRequest
+        if (!canMakeRequest("setVoNrEnabled", voiceProxy, null, RADIO_HAL_VERSION_2_0)) {
             /* calling a query api to let HAL know that VoNREnabled state is updated.
                This is a work around as new AIDL API is not allowed for older HAL version devices.
                HAL can check the value of PROPERTY_IS_VONR_ENABLED property to determine
@@ -3268,858 +3075,839 @@
                 AsyncResult.forMessage(result, null, null);
                 result.sendToTarget();
             }
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_VONR, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "setVoNrEnabled", () -> {
+            voiceProxy.setVoNrEnabled(rr.mSerial, enabled);
+        });
     }
 
     @Override
     public void setCdmaSubscriptionSource(int cdmaSubscription, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " cdmaSubscription = " + cdmaSubscription);
-            }
-
-            try {
-                simProxy.setCdmaSubscriptionSource(rr.mSerial, cdmaSubscription);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setCdmaSubscriptionSource", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("setCdmaSubscriptionSource", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " cdmaSubscription = " + cdmaSubscription);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "setCdmaSubscriptionSource", () -> {
+            simProxy.setCdmaSubscriptionSource(rr.mSerial, cdmaSubscription);
+        });
     }
 
     @Override
     public void queryCdmaRoamingPreference(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getCdmaRoamingPreference(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "queryCdmaRoamingPreference", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("queryCdmaRoamingPreference", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "queryCdmaRoamingPreference", () -> {
+            networkProxy.getCdmaRoamingPreference(rr.mSerial);
+        });
     }
 
     @Override
     public void setCdmaRoamingPreference(int cdmaRoamingType, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " cdmaRoamingType = " + cdmaRoamingType);
-            }
-
-            try {
-                networkProxy.setCdmaRoamingPreference(rr.mSerial, cdmaRoamingType);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setCdmaRoamingPreference", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setCdmaRoamingPreference", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " cdmaRoamingType = " + cdmaRoamingType);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setCdmaRoamingPreference", () -> {
+            networkProxy.setCdmaRoamingPreference(rr.mSerial, cdmaRoamingType);
+        });
     }
 
     @Override
     public void queryTTYMode(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_TTY_MODE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.getTtyMode(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getTtyMode", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("queryTTYMode", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_TTY_MODE, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "queryTTYMode", () -> {
+            voiceProxy.getTtyMode(rr.mSerial);
+        });
     }
 
     @Override
     public void setTTYMode(int ttyMode, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_TTY_MODE, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " ttyMode = " + ttyMode);
-            }
-
-            try {
-                voiceProxy.setTtyMode(rr.mSerial, ttyMode);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setTtyMode", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("setTTYMode", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_TTY_MODE, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " ttyMode = " + ttyMode);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "setTTYMode", () -> {
+            voiceProxy.setTtyMode(rr.mSerial, ttyMode);
+        });
     }
 
     @Override
     public void setPreferredVoicePrivacy(boolean enable, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " enable = " + enable);
-            }
-
-            try {
-                voiceProxy.setPreferredVoicePrivacy(rr.mSerial, enable);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setPreferredVoicePrivacy", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("setPreferredVoicePrivacy", voiceProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " enable = " + enable);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "setPreferredVoicePrivacy", () -> {
+            voiceProxy.setPreferredVoicePrivacy(rr.mSerial, enable);
+        });
     }
 
     @Override
     public void getPreferredVoicePrivacy(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE,
-                    result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.getPreferredVoicePrivacy(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getPreferredVoicePrivacy", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("getPreferredVoicePrivacy", voiceProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE,
+                result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "getPreferredVoicePrivacy", () -> {
+            voiceProxy.getPreferredVoicePrivacy(rr.mSerial);
+        });
     }
 
     @Override
     public void sendCDMAFeatureCode(String featureCode, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_FLASH, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " featureCode = " + Rlog.pii(RILJ_LOG_TAG, featureCode));
-            }
-
-            try {
-                voiceProxy.sendCdmaFeatureCode(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(featureCode));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendCdmaFeatureCode", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("sendCDMAFeatureCode", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_FLASH, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " featureCode = " + Rlog.pii(RILJ_LOG_TAG, featureCode));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "sendCDMAFeatureCode", () -> {
+            voiceProxy.sendCdmaFeatureCode(rr.mSerial,
+                    RILUtils.convertNullToEmptyString(featureCode));
+        });
     }
 
     @Override
     public void sendBurstDtmf(String dtmfString, int on, int off, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_BURST_DTMF, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " dtmfString = " + dtmfString + " on = " + on + " off = " + off);
-            }
-
-            try {
-                voiceProxy.sendBurstDtmf(rr.mSerial, RILUtils.convertNullToEmptyString(dtmfString),
-                        on, off);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendBurstDtmf", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("sendBurstDtmf", voiceProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_BURST_DTMF, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " dtmfString = " + dtmfString + " on = " + on + " off = " + off);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "sendBurstDtmf", () -> {
+            voiceProxy.sendBurstDtmf(rr.mSerial, RILUtils.convertNullToEmptyString(dtmfString),
+                    on, off);
+        });
     }
 
     @Override
     public void sendCdmaSMSExpectMore(byte[] pdu, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE, result,
-                    mRILDefaultWorkSource);
-
-            // Do not log function arg for privacy
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                messagingProxy.sendCdmaSmsExpectMore(rr.mSerial, pdu);
-                if (mHalVersion.get(HAL_SERVICE_MESSAGING).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
-                    mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
-                            SmsSession.Event.Format.SMS_FORMAT_3GPP2,
-                            getOutgoingSmsMessageId(result));
-                }
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendCdmaSMSExpectMore", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("sendCdmaSMSExpectMore", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE, result,
+                mRILDefaultWorkSource);
+
+        // Do not log function arg for privacy
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "sendCdmaSMSExpectMore", () -> {
+            messagingProxy.sendCdmaSmsExpectMore(rr.mSerial, pdu);
+            if (mHalVersion.get(HAL_SERVICE_MESSAGING).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
+                        SmsSession.Event.Format.SMS_FORMAT_3GPP2,
+                        getOutgoingSmsMessageId(result));
+            }
+        });
     }
 
     @Override
     public void sendCdmaSms(byte[] pdu, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SEND_SMS, result, mRILDefaultWorkSource);
-
-            // Do not log function arg for privacy
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                messagingProxy.sendCdmaSms(rr.mSerial, pdu);
-                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
-                        SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendCdmaSms", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("sendCdmaSms", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SEND_SMS, result, mRILDefaultWorkSource);
+
+        // Do not log function arg for privacy
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "sendCdmaSms", () -> {
+            messagingProxy.sendCdmaSms(rr.mSerial, pdu);
+            mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
+                    SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result));
+        });
     }
 
     @Override
     public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " success = " + success + " cause = " + cause);
-            }
-
-            try {
-                messagingProxy.acknowledgeLastIncomingCdmaSms(rr.mSerial, success, cause);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
-                        "acknowledgeLastIncomingCdmaSms", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("acknowledgeLastIncomingCdmaSms", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " success = " + success + " cause = " + cause);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "acknowledgeLastIncomingCdmaSms",
+                () -> {
+                    messagingProxy.acknowledgeLastIncomingCdmaSms(rr.mSerial, success, cause);
+                });
     }
 
     @Override
     public void getGsmBroadcastConfig(Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GSM_GET_BROADCAST_CONFIG, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                messagingProxy.getGsmBroadcastConfig(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getGsmBroadcastConfig", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("getGsmBroadcastConfig", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GSM_GET_BROADCAST_CONFIG, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "getGsmBroadcastConfig", () -> {
+            messagingProxy.getGsmBroadcastConfig(rr.mSerial);
+        });
     }
 
     @Override
     public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GSM_SET_BROADCAST_CONFIG, result,
-                    mRILDefaultWorkSource);
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("setGsmBroadcastConfig", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
+        }
 
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " with " + config.length + " configs : ");
-                for (int i = 0; i < config.length; i++) {
-                    riljLog(config[i].toString());
-                }
-            }
+        RILRequest rr = obtainRequest(RIL_REQUEST_GSM_SET_BROADCAST_CONFIG, result,
+                mRILDefaultWorkSource);
 
-            try {
-                messagingProxy.setGsmBroadcastConfig(rr.mSerial, config);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setGsmBroadcastConfig", e);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " with " + config.length + " configs : ");
+            for (int i = 0; i < config.length; i++) {
+                riljLog(config[i].toString());
             }
         }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "setGsmBroadcastConfig", () -> {
+            messagingProxy.setGsmBroadcastConfig(rr.mSerial, config);
+        });
     }
 
     @Override
     public void setGsmBroadcastActivation(boolean activate, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GSM_BROADCAST_ACTIVATION, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " activate = " + activate);
-            }
-
-            try {
-                messagingProxy.setGsmBroadcastActivation(rr.mSerial, activate);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
-                        "setGsmBroadcastActivation", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("setGsmBroadcastActivation", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GSM_BROADCAST_ACTIVATION, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " activate = " + activate);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "setGsmBroadcastActivation", () -> {
+            messagingProxy.setGsmBroadcastActivation(rr.mSerial, activate);
+        });
     }
 
     @Override
     public void getCdmaBroadcastConfig(Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                messagingProxy.getCdmaBroadcastConfig(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getCdmaBroadcastConfig", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("getCdmaBroadcastConfig", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "getCdmaBroadcastConfig", () -> {
+            messagingProxy.getCdmaBroadcastConfig(rr.mSerial);
+        });
     }
 
     @Override
     public void setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG, result,
-                    mRILDefaultWorkSource);
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("setCdmaBroadcastConfig", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
+        }
 
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " with " + configs.length + " configs : ");
-                for (CdmaSmsBroadcastConfigInfo config : configs) {
-                    riljLog(config.toString());
-                }
-            }
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG, result,
+                mRILDefaultWorkSource);
 
-            try {
-                messagingProxy.setCdmaBroadcastConfig(rr.mSerial, configs);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setCdmaBroadcastConfig", e);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " with " + configs.length + " configs : ");
+            for (CdmaSmsBroadcastConfigInfo config : configs) {
+                riljLog(config.toString());
             }
         }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "setCdmaBroadcastConfig", () -> {
+            messagingProxy.setCdmaBroadcastConfig(rr.mSerial, configs);
+        });
     }
 
     @Override
     public void setCdmaBroadcastActivation(boolean activate, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_BROADCAST_ACTIVATION, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " activate = " + activate);
-            }
-
-            try {
-                messagingProxy.setCdmaBroadcastActivation(rr.mSerial, activate);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
-                        "setCdmaBroadcastActivation", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("setCdmaBroadcastActivation", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_BROADCAST_ACTIVATION, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " activate = " + activate);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "setCdmaBroadcastActivation", () -> {
+            messagingProxy.setCdmaBroadcastActivation(rr.mSerial, activate);
+        });
     }
 
     @Override
     public void getCDMASubscription(Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SUBSCRIPTION, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                simProxy.getCdmaSubscription(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getCdmaSubscription", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("getCDMASubscription", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SUBSCRIPTION, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "getCDMASubscription", () -> {
+            simProxy.getCdmaSubscription(rr.mSerial);
+        });
     }
 
     @Override
     public void writeSmsToRuim(int status, byte[] pdu, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGV) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " status = " + status);
-            }
-
-            try {
-                messagingProxy.writeSmsToRuim(rr.mSerial, status, pdu);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "writeSmsToRuim", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("writeSmsToRuim", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGV) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " status = " + status);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "writeSmsToRuim", () -> {
+            messagingProxy.writeSmsToRuim(rr.mSerial, status, pdu);
+        });
     }
 
     @Override
     public void deleteSmsOnRuim(int index, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGV) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " index = " + index);
-            }
-
-            try {
-                messagingProxy.deleteSmsOnRuim(rr.mSerial, index);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "deleteSmsOnRuim", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("deleteSmsOnRuim", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGV) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " index = " + index);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "deleteSmsOnRuim", () -> {
+            messagingProxy.deleteSmsOnRuim(rr.mSerial, index);
+        });
     }
 
     @Override
     public void getDeviceIdentity(Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_DEVICE_IDENTITY, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                modemProxy.getDeviceIdentity(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getDeviceIdentity", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("getDeviceIdentity", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_DEVICE_IDENTITY, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "getDeviceIdentity", () -> {
+            modemProxy.getDeviceIdentity(rr.mSerial);
+        });
     }
 
     @Override
     public void getImei(Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (modemProxy.isEmpty()) {
-            if (RILJ_LOGD) {
-                Rlog.e(RILJ_LOG_TAG, "getImei: modemProxy is Empty");
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("getImei", modemProxy, result, RADIO_HAL_VERSION_2_1)) {
             return;
         }
-        if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_DEVICE_IMEI, result,
-                    mRILDefaultWorkSource);
 
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
+        RILRequest rr = obtainRequest(RIL_REQUEST_DEVICE_IMEI, result, mRILDefaultWorkSource);
 
-            try {
-                modemProxy.getImei(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getImei", e);
-            }
-        }  else {
-            if (RILJ_LOGD) {
-                Rlog.e(RILJ_LOG_TAG, "getImei: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
         }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "getImei", () -> {
+            modemProxy.getImei(rr.mSerial);
+        });
     }
 
     @Override
     public void exitEmergencyCallbackMode(Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.exitEmergencyCallbackMode(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "exitEmergencyCallbackMode", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("exitEmergencyCallbackMode", voiceProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "exitEmergencyCallbackMode", () -> {
+            voiceProxy.exitEmergencyCallbackMode(rr.mSerial);
+        });
     }
 
     @Override
     public void getSmscAddress(Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_SMSC_ADDRESS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                messagingProxy.getSmscAddress(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getSmscAddress", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("getSmscAddress", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_SMSC_ADDRESS, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "getSmscAddress", () -> {
+            messagingProxy.getSmscAddress(rr.mSerial);
+        });
     }
 
     @Override
     public void setSmscAddress(String address, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SMSC_ADDRESS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " address = " + address);
-            }
-
-            try {
-                messagingProxy.setSmscAddress(rr.mSerial,
-                        RILUtils.convertNullToEmptyString(address));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setSmscAddress", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("setSmscAddress", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SMSC_ADDRESS, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " address = " + address);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "setSmscAddress", () -> {
+            messagingProxy.setSmscAddress(rr.mSerial, RILUtils.convertNullToEmptyString(address));
+        });
     }
 
     @Override
     public void reportSmsMemoryStatus(boolean available, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " available = " + available);
-            }
-
-            try {
-                messagingProxy.reportSmsMemoryStatus(rr.mSerial, available);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "reportSmsMemoryStatus", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("reportSmsMemoryStatus", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " available = " + available);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "reportSmsMemoryStatus", () -> {
+            messagingProxy.reportSmsMemoryStatus(rr.mSerial, available);
+        });
     }
 
     @Override
     public void reportStkServiceIsRunning(Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                simProxy.reportStkServiceIsRunning(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "reportStkServiceIsRunning", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("reportStkServiceIsRunning", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "reportStkServiceIsRunning", () -> {
+            simProxy.reportStkServiceIsRunning(rr.mSerial);
+        });
     }
 
     @Override
     public void getCdmaSubscriptionSource(Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                simProxy.getCdmaSubscriptionSource(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getCdmaSubscriptionSource", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("getCdmaSubscriptionSource", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "getCdmaSubscriptionSource", () -> {
+            simProxy.getCdmaSubscriptionSource(rr.mSerial);
+        });
     }
 
     @Override
     public void acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " success = " + success);
-            }
-
-            try {
-                messagingProxy.acknowledgeIncomingGsmSmsWithPdu(rr.mSerial, success,
-                        RILUtils.convertNullToEmptyString(ackPdu));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
-                        "acknowledgeIncomingGsmSmsWithPdu", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("acknowledgeIncomingGsmSmsWithPdu", messagingProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " success = " + success);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "acknowledgeIncomingGsmSmsWithPdu",
+                () -> {
+                    messagingProxy.acknowledgeIncomingGsmSmsWithPdu(rr.mSerial, success,
+                            RILUtils.convertNullToEmptyString(ackPdu));
+                });
     }
 
     @Override
     public void getVoiceRadioTechnology(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_VOICE_RADIO_TECH, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getVoiceRadioTechnology(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getVoiceRadioTechnology", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getVoiceRadioTechnology", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_VOICE_RADIO_TECH, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getVoiceRadioTechnology", () -> {
+            networkProxy.getVoiceRadioTechnology(rr.mSerial);
+        });
     }
 
     @Override
     public void getCellInfoList(Message result, WorkSource workSource) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_CELL_INFO_LIST, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getCellInfoList(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getCellInfoList", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getCellInfoList", networkProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_CELL_INFO_LIST, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getCellInfoList", () -> {
+            networkProxy.getCellInfoList(rr.mSerial);
+        });
     }
 
     @Override
     public void setCellInfoListRate(int rateInMillis, Message result, WorkSource workSource) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " rateInMillis = " + rateInMillis);
-            }
-
-            try {
-                networkProxy.setCellInfoListRate(rr.mSerial, rateInMillis);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setCellInfoListRate", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setCellInfoListRate", networkProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " rateInMillis = " + rateInMillis);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setCellInfoListRate", () -> {
+            networkProxy.setCellInfoListRate(rr.mSerial, rateInMillis);
+        });
     }
 
     @Override
-    public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (!dataProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_INITIAL_ATTACH_APN, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + dataProfile);
-            }
-
-            try {
-                dataProxy.setInitialAttachApn(rr.mSerial, dataProfile, isRoaming);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setInitialAttachApn", e);
-            }
+    public void setInitialAttachApn(DataProfile dataProfile, Message result) {
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("setInitialAttachApn", dataProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_INITIAL_ATTACH_APN, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + dataProfile);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "setInitialAttachApn", () -> {
+            dataProxy.setInitialAttachApn(rr.mSerial, dataProfile);
+        });
     }
 
     @Override
     public void getImsRegistrationState(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_IMS_REGISTRATION_STATE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getImsRegistrationState(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getImsRegistrationState", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getImsRegistrationState", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_IMS_REGISTRATION_STATE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getImsRegistrationState", () -> {
+            networkProxy.getImsRegistrationState(rr.mSerial);
+        });
     }
 
     @Override
     public void sendImsGsmSms(String smscPdu, String pdu, int retry, int messageRef,
             Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_IMS_SEND_SMS, result, mRILDefaultWorkSource);
-
-            // Do not log function args for privacy
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                messagingProxy.sendImsSms(rr.mSerial, smscPdu, pdu, null, retry, messageRef);
-                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
-                        SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendImsGsmSms", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("sendImsGsmSms", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_IMS_SEND_SMS, result, mRILDefaultWorkSource);
+
+        // Do not log function args for privacy
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "sendImsGsmSms", () -> {
+            messagingProxy.sendImsSms(rr.mSerial, smscPdu, pdu, null, retry, messageRef);
+            mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
+                    SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
+        });
     }
 
     @Override
     public void sendImsCdmaSms(byte[] pdu, int retry, int messageRef, Message result) {
-        RadioMessagingProxy messagingProxy =
-                getRadioServiceProxy(RadioMessagingProxy.class, result);
-        if (!messagingProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_IMS_SEND_SMS, result, mRILDefaultWorkSource);
-
-            // Do not log function args for privacy
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                messagingProxy.sendImsSms(rr.mSerial, null, null, pdu, retry, messageRef);
-                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
-                        SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendImsCdmaSms", e);
-            }
+        RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class);
+        if (!canMakeRequest("sendImsCdmaSms", messagingProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_IMS_SEND_SMS, result, mRILDefaultWorkSource);
+
+        // Do not log function args for privacy
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MESSAGING, rr, "sendImsCdmaSms", () -> {
+            messagingProxy.sendImsSms(rr.mSerial, null, null, pdu, retry, messageRef);
+            mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
+                    SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result));
+        });
     }
 
     @Override
     public void iccTransmitApduBasicChannel(int cla, int instruction, int p1, int p2, int p3,
             String data, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC, result,
-                    mRILDefaultWorkSource);
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("iccTransmitApduBasicChannel", simProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
+        }
 
-            if (RILJ_LOGD) {
-                if (TelephonyUtils.IS_DEBUGGABLE) {
-                    riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                            + String.format(" cla = 0x%02X ins = 0x%02X", cla, instruction)
-                            + String.format(" p1 = 0x%02X p2 = 0x%02X p3 = 0x%02X", p1, p2, p3)
-                            + " data = " + data);
-                } else {
-                    riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-                }
-            }
+        RILRequest rr = obtainRequest(RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC, result,
+                mRILDefaultWorkSource);
 
-            try {
-                simProxy.iccTransmitApduBasicChannel(
-                        rr.mSerial, cla, instruction, p1, p2, p3, data);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccTransmitApduBasicChannel", e);
+        if (RILJ_LOGD) {
+            if (TelephonyUtils.IS_DEBUGGABLE) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                        + String.format(" cla = 0x%02X ins = 0x%02X", cla, instruction)
+                        + String.format(" p1 = 0x%02X p2 = 0x%02X p3 = 0x%02X", p1, p2, p3)
+                        + " data = " + data);
+            } else {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
             }
         }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "iccTransmitApduBasicChannel", () -> {
+            simProxy.iccTransmitApduBasicChannel(rr.mSerial, cla, instruction, p1, p2, p3, data);
+        });
     }
 
     @Override
     public void iccOpenLogicalChannel(String aid, int p2, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_OPEN_CHANNEL, result,
-                    mRILDefaultWorkSource);
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("iccOpenLogicalChannel", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
+        }
 
-            if (RILJ_LOGD) {
-                if (TelephonyUtils.IS_DEBUGGABLE) {
-                    riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                            + " aid = " + aid + " p2 = " + p2);
-                } else {
-                    riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-                }
-            }
+        RILRequest rr = obtainRequest(RIL_REQUEST_SIM_OPEN_CHANNEL, result, mRILDefaultWorkSource);
 
-            try {
-                simProxy.iccOpenLogicalChannel(rr.mSerial, RILUtils.convertNullToEmptyString(aid),
-                        p2);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccOpenLogicalChannel", e);
+        if (RILJ_LOGD) {
+            if (TelephonyUtils.IS_DEBUGGABLE) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                        + " aid = " + aid + " p2 = " + p2);
+            } else {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
             }
         }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "iccOpenLogicalChannel", () -> {
+            simProxy.iccOpenLogicalChannel(rr.mSerial, RILUtils.convertNullToEmptyString(aid), p2);
+        });
     }
 
     @Override
     public void iccCloseLogicalChannel(int channel, boolean isEs10, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_CLOSE_CHANNEL, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " channel = " + channel + " isEs10 = " + isEs10);
-            }
-            try {
-                simProxy.iccCloseLogicalChannel(rr.mSerial, channel, isEs10);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccCloseLogicalChannel", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("iccCloseLogicalChannel", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SIM_CLOSE_CHANNEL, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " channel = " + channel + " isEs10 = " + isEs10);
+        }
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "iccCloseLogicalChannel", () -> {
+            simProxy.iccCloseLogicalChannel(rr.mSerial, channel, isEs10);
+        });
     }
 
     @Override
@@ -4130,351 +3918,276 @@
                     "Invalid channel in iccTransmitApduLogicalChannel: " + channel);
         }
 
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL, result,
-                    mRILDefaultWorkSource);
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("iccTransmitApduLogicalChannel", simProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
+        }
 
-            if (RILJ_LOGD) {
-                if (TelephonyUtils.IS_DEBUGGABLE) {
-                    riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                            + String.format(" channel = %d", channel)
-                            + String.format(" cla = 0x%02X ins = 0x%02X", cla, instruction)
-                            + String.format(" p1 = 0x%02X p2 = 0x%02X p3 = 0x%02X", p1, p2, p3)
-                            + " isEs10Command = " + isEs10Command
-                            + " data = " + data);
-                } else {
-                    riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-                }
-            }
+        RILRequest rr = obtainRequest(RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL, result,
+                mRILDefaultWorkSource);
 
-            try {
-                simProxy.iccTransmitApduLogicalChannel(
-                        rr.mSerial, channel, cla, instruction, p1, p2, p3, data, isEs10Command);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccTransmitApduLogicalChannel", e);
+        if (RILJ_LOGD) {
+            if (TelephonyUtils.IS_DEBUGGABLE) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                        + String.format(" channel = %d", channel)
+                        + String.format(" cla = 0x%02X ins = 0x%02X", cla, instruction)
+                        + String.format(" p1 = 0x%02X p2 = 0x%02X p3 = 0x%02X", p1, p2, p3)
+                        + " isEs10Command = " + isEs10Command
+                        + " data = " + data);
+            } else {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
             }
         }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "iccTransmitApduLogicalChannel", () -> {
+            simProxy.iccTransmitApduLogicalChannel(rr.mSerial, channel, cla, instruction, p1, p2,
+                    p3, data, isEs10Command);
+        });
     }
 
     @Override
     public void nvReadItem(int itemID, Message result, WorkSource workSource) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_NV_READ_ITEM, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " itemId = " + itemID);
-            }
-
-            try {
-                modemProxy.nvReadItem(rr.mSerial, itemID);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvReadItem", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("nvReadItem", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_NV_READ_ITEM, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " itemId = " + itemID);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "nvReadItem", () -> {
+            modemProxy.nvReadItem(rr.mSerial, itemID);
+        });
     }
 
     @Override
     public void nvWriteItem(int itemId, String itemValue, Message result, WorkSource workSource) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_NV_WRITE_ITEM, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " itemId = " + itemId + " itemValue = " + itemValue);
-            }
-
-            try {
-                modemProxy.nvWriteItem(rr.mSerial, itemId,
-                        RILUtils.convertNullToEmptyString(itemValue));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvWriteItem", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("nvWriteItem", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_NV_WRITE_ITEM, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " itemId = " + itemId + " itemValue = " + itemValue);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "nvWriteItem", () -> {
+            modemProxy.nvWriteItem(rr.mSerial, itemId,
+                    RILUtils.convertNullToEmptyString(itemValue));
+        });
     }
 
     @Override
     public void nvWriteCdmaPrl(byte[] preferredRoamingList, Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_NV_WRITE_CDMA_PRL, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " PreferredRoamingList = 0x"
-                        + IccUtils.bytesToHexString(preferredRoamingList));
-            }
-
-            try {
-                modemProxy.nvWriteCdmaPrl(rr.mSerial, preferredRoamingList);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvWriteCdmaPrl", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("nvWriteCdmaPrl", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_NV_WRITE_CDMA_PRL, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " PreferredRoamingList = 0x"
+                    + IccUtils.bytesToHexString(preferredRoamingList));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "nvWriteCdmaPrl", () -> {
+            modemProxy.nvWriteCdmaPrl(rr.mSerial, preferredRoamingList);
+        });
     }
 
     @Override
     public void nvResetConfig(int resetType, Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_NV_RESET_CONFIG, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " resetType = " + resetType);
-            }
-
-            try {
-                modemProxy.nvResetConfig(rr.mSerial, resetType);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvResetConfig", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("nvResetConfig", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_NV_RESET_CONFIG, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " resetType = " + resetType);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "nvResetConfig", () -> {
+            modemProxy.nvResetConfig(rr.mSerial, resetType);
+        });
     }
 
     @Override
     public void setUiccSubscription(int slotId, int appIndex, int subId, int subStatus,
             Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_UICC_SUBSCRIPTION, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " slot = " + slotId + " appIndex = " + appIndex
-                        + " subId = " + subId + " subStatus = " + subStatus);
-            }
-
-            try {
-                simProxy.setUiccSubscription(rr.mSerial, slotId, appIndex, subId, subStatus);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setUiccSubscription", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("setUiccSubscription", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
-    }
 
-    /**
-     * Whether the device modem supports reporting the EID in either the slot or card status or
-     * through ATR.
-     * @return true if the modem supports EID.
-     */
-    @Override
-    public boolean supportsEid() {
-        // EID should be supported as long as HAL >= 1.2.
-        //  - in HAL 1.2 we have EID through ATR
-        //  - in later HAL versions we also have EID through slot / card status.
-        return mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2);
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_UICC_SUBSCRIPTION, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " slot = " + slotId + " appIndex = " + appIndex
+                    + " subId = " + subId + " subStatus = " + subStatus);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "setUiccSubscription", () -> {
+            simProxy.setUiccSubscription(rr.mSerial, slotId, appIndex, subId, subStatus);
+        });
     }
 
     @Override
     public void setDataAllowed(boolean allowed, Message result) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (!dataProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ALLOW_DATA, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " allowed = " + allowed);
-            }
-
-            try {
-                dataProxy.setDataAllowed(rr.mSerial, allowed);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataAllowed", e);
-            }
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("setDataAllowed", dataProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ALLOW_DATA, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " allowed = " + allowed);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "setDataAllowed", () -> {
+            dataProxy.setDataAllowed(rr.mSerial, allowed);
+        });
     }
 
     @Override
     public void getHardwareConfig(Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_HARDWARE_CONFIG, result,
-                    mRILDefaultWorkSource);
-
-            // Do not log function args for privacy
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                modemProxy.getHardwareConfig(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getHardwareConfig", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("getHardwareConfig", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_HARDWARE_CONFIG, result,
+                mRILDefaultWorkSource);
+
+        // Do not log function args for privacy
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "getHardwareConfig", () -> {
+            modemProxy.getHardwareConfig(rr.mSerial);
+        });
     }
 
     @Override
     public void requestIccSimAuthentication(int authContext, String data, String aid,
             Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_AUTHENTICATION, result,
-                    mRILDefaultWorkSource);
-
-            // Do not log function args for privacy
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                simProxy.requestIccSimAuthentication(rr.mSerial, authContext,
-                        RILUtils.convertNullToEmptyString(data),
-                        RILUtils.convertNullToEmptyString(aid));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "requestIccSimAuthentication", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("requestIccSimAuthentication", simProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SIM_AUTHENTICATION, result,
+                mRILDefaultWorkSource);
+
+        // Do not log function args for privacy
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "requestIccSimAuthentication", () -> {
+            simProxy.requestIccSimAuthentication(rr.mSerial, authContext,
+                    RILUtils.convertNullToEmptyString(data),
+                    RILUtils.convertNullToEmptyString(aid));
+        });
     }
 
     @Override
-    public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (!dataProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
-                    mRILDefaultWorkSource);
+    public void setDataProfile(DataProfile[] dps, Message result) {
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("setDataProfile", dataProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
+        }
 
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " with data profiles : ");
-                for (DataProfile profile : dps) {
-                    riljLog(profile.toString());
-                }
-            }
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result, mRILDefaultWorkSource);
 
-            try {
-                dataProxy.setDataProfile(rr.mSerial, dps, isRoaming);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataProfile", e);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " with data profiles : ");
+            for (DataProfile profile : dps) {
+                riljLog(profile.toString());
             }
         }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "setDataProfile", () -> {
+            dataProxy.setDataProfile(rr.mSerial, dps);
+        });
     }
 
     @Override
     public void requestShutdown(Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SHUTDOWN, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                modemProxy.requestShutdown(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "requestShutdown", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("requestShutdown", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SHUTDOWN, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "requestShutdown", () -> {
+            modemProxy.requestShutdown(rr.mSerial);
+        });
     }
 
     @Override
     public void getRadioCapability(Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_RADIO_CAPABILITY, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                modemProxy.getRadioCapability(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getRadioCapability", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("getRadioCapability", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_RADIO_CAPABILITY, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "getRadioCapability", () -> {
+            modemProxy.getRadioCapability(rr.mSerial);
+        });
     }
 
     @Override
     public void setRadioCapability(RadioCapability rc, Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_RADIO_CAPABILITY, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " RadioCapability = " + rc.toString());
-            }
-
-            try {
-                modemProxy.setRadioCapability(rr.mSerial, rc);
-            } catch (Exception e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "setRadioCapability", e);
-            }
-        }
-    }
-
-    @Override
-    public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
-        if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
-            // We have a 1.2 or later radio, so the LCE 1.0 LCE service control path is unused.
-            // Instead the LCE functionality is always-on and provides unsolicited indications.
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startLceService: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("setRadioCapability", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
             return;
         }
 
-        IRadio radioProxy = getRadioProxy(result);
-        if (radioProxy != null) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_START_LCE, result, mRILDefaultWorkSource);
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_RADIO_CAPABILITY, result,
+                mRILDefaultWorkSource);
 
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " reportIntervalMs = " + reportIntervalMs + " pullMode = " + pullMode);
-            }
-
-            try {
-                radioProxy.startLceService(rr.mSerial, reportIntervalMs, pullMode);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "startLceService", e);
-            }
-        }
-    }
-
-    @Override
-    public void stopLceService(Message result) {
-        if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
-            // We have a 1.2 or later radio, so the LCE 1.0 LCE service control is unused.
-            // Instead the LCE functionality is always-on and provides unsolicited indications.
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopLceService: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
-            return;
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " RadioCapability = " + rc.toString());
         }
 
-        IRadio radioProxy = getRadioProxy(result);
-        if (radioProxy != null) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_STOP_LCE, result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                radioProxy.stopLceService(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "stopLceService", e);
-            }
-        }
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "setRadioCapability", () -> {
+            modemProxy.setRadioCapability(rr.mSerial, rc);
+        });
     }
 
     /**
@@ -4489,96 +4202,45 @@
     @Override
     public void setDataThrottling(Message result, WorkSource workSource, int dataThrottlingAction,
             long completionWindowMillis) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (dataProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_THROTTLING, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> "
-                        + RILUtils.requestToString(rr.mRequest)
-                        + " dataThrottlingAction = " + dataThrottlingAction
-                        + " completionWindowMillis " + completionWindowMillis);
-            }
-
-            try {
-                dataProxy.setDataThrottling(rr.mSerial, (byte) dataThrottlingAction,
-                        completionWindowMillis);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataThrottling", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "setDataThrottling: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
-        }
-    }
-
-    /**
-     * This will only be called if the LCE service is started in PULL mode, which is
-     * only enabled when using Radio HAL versions 1.1 and earlier.
-     *
-     * It is still possible for vendors to override this behavior and use the 1.1 version
-     * of LCE; however, this is strongly discouraged and this functionality will be removed
-     * when HAL 1.x support is dropped.
-     *
-     * @deprecated HAL 1.2 and later use an always-on LCE that relies on indications.
-     */
-    @Deprecated
-    @Override
-    public void pullLceData(Message result) {
-        if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
-            // We have a 1.2 or later radio, so the LCE 1.0 LCE service control path is unused.
-            // Instead the LCE functionality is always-on and provides unsolicited indications.
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "pullLceData: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("setDataThrottling", dataProxy, result, RADIO_HAL_VERSION_1_6)) {
             return;
         }
 
-        IRadio radioProxy = getRadioProxy(result);
-        if (radioProxy != null) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_PULL_LCEDATA, result, mRILDefaultWorkSource);
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_THROTTLING, result,
+                getDefaultWorkSourceIfInvalid(workSource));
 
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                radioProxy.pullLceData(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "pullLceData", e);
-            }
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " dataThrottlingAction = " + dataThrottlingAction
+                    + " completionWindowMillis " + completionWindowMillis);
         }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "setDataThrottling", () -> {
+            dataProxy.setDataThrottling(rr.mSerial, (byte) dataThrottlingAction,
+                    completionWindowMillis);
+        });
     }
 
     @Override
     public void getModemActivityInfo(Message result, WorkSource workSource) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_ACTIVITY_INFO, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                modemProxy.getModemActivityInfo(rr.mSerial);
-                Message msg =
-                        mRilHandler.obtainMessage(EVENT_BLOCKING_RESPONSE_TIMEOUT, rr.mSerial);
-                mRilHandler.sendMessageDelayed(msg, DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getModemActivityInfo", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("getModemActivityInfo", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_ACTIVITY_INFO, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "getModemActivityInfo", () -> {
+            modemProxy.getModemActivityInfo(rr.mSerial);
+            Message msg = mRilHandler.obtainMessage(EVENT_BLOCKING_RESPONSE_TIMEOUT, rr.mSerial);
+            mRilHandler.sendMessageDelayed(msg, DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS);
+        });
     }
 
     @Override
@@ -4586,266 +4248,208 @@
             Message result, WorkSource workSource) {
         Objects.requireNonNull(carrierRestrictionRules, "Carrier restriction cannot be null.");
 
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_ALLOWED_CARRIERS, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " params: " + carrierRestrictionRules);
-            }
-
-            try {
-                simProxy.setAllowedCarriers(rr.mSerial, carrierRestrictionRules, result);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setAllowedCarriers", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("setAllowedCarriers", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_ALLOWED_CARRIERS, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " params: " + carrierRestrictionRules);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "setAllowedCarriers", () -> {
+            simProxy.setAllowedCarriers(rr.mSerial, carrierRestrictionRules);
+        });
     }
 
     @Override
     public void getAllowedCarriers(Message result, WorkSource workSource) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_ALLOWED_CARRIERS, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                simProxy.getAllowedCarriers(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getAllowedCarriers", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("getAllowedCarriers", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_ALLOWED_CARRIERS, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "getAllowedCarriers", () -> {
+            simProxy.getAllowedCarriers(rr.mSerial);
+        });
     }
 
     @Override
     public void sendDeviceState(int stateType, boolean state, Message result) {
-        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
-        if (!modemProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SEND_DEVICE_STATE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + " "
-                        + stateType + ":" + state);
-            }
-
-            try {
-                modemProxy.sendDeviceState(rr.mSerial, stateType, state);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "sendDeviceState", e);
-            }
+        RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class);
+        if (!canMakeRequest("sendDeviceState", modemProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SEND_DEVICE_STATE, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + " "
+                    + stateType + ":" + state);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_MODEM, rr, "sendDeviceState", () -> {
+            modemProxy.sendDeviceState(rr.mSerial, stateType, state);
+        });
     }
 
     @Override
     public void setUnsolResponseFilter(int filter, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (!networkProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " " + filter);
-            }
-
-            try {
-                networkProxy.setIndicationFilter(rr.mSerial, filter);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setIndicationFilter", e);
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setUnsolResponseFilter", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " " + filter);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setUnsolResponseFilter", () -> {
+            networkProxy.setIndicationFilter(rr.mSerial, filter);
+        });
     }
 
     @Override
     public void setSignalStrengthReportingCriteria(
             @NonNull List<SignalThresholdInfo> signalThresholdInfos, @Nullable Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA,
-                    result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.setSignalStrengthReportingCriteria(rr.mSerial, signalThresholdInfos);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "setSignalStrengthReportingCriteria", e);
-            }
-        } else {
-            riljLoge("setSignalStrengthReportingCriteria ignored on IRadio version less than 1.2");
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setSignalStrengthReportingCriteria", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setSignalStrengthReportingCriteria",
+                () -> {
+                    networkProxy.setSignalStrengthReportingCriteria(rr.mSerial,
+                            signalThresholdInfos);
+                });
     }
 
     @Override
     public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
             int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
             Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.setLinkCapacityReportingCriteria(rr.mSerial, hysteresisMs,
-                        hysteresisDlKbps, hysteresisUlKbps, thresholdsDlKbps, thresholdsUlKbps,
-                        ran);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(
-                        HAL_SERVICE_NETWORK, "setLinkCapacityReportingCriteria", e);
-            }
-        } else {
-            riljLoge("setLinkCapacityReportingCriteria ignored on IRadio version less than 1.2");
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setLinkCapacityReportingCriteria", networkProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setLinkCapacityReportingCriteria",
+                () -> {
+                    networkProxy.setLinkCapacityReportingCriteria(rr.mSerial, hysteresisMs,
+                            hysteresisDlKbps, hysteresisUlKbps, thresholdsDlKbps, thresholdsUlKbps,
+                            ran);
+                });
     }
 
     @Override
     public void setSimCardPower(int state, Message result, WorkSource workSource) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (!simProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIM_CARD_POWER, result,
-                    getDefaultWorkSourceIfInvalid(workSource));
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " " + state);
-            }
-
-            try {
-                simProxy.setSimCardPower(rr.mSerial, state, result);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setSimCardPower", e);
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("setSimCardPower", simProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIM_CARD_POWER, result,
+                getDefaultWorkSourceIfInvalid(workSource));
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " " + state);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "setSimCardPower", () -> {
+            simProxy.setSimCardPower(rr.mSerial, state);
+        });
     }
 
     @Override
     public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
             Message result) {
         Objects.requireNonNull(imsiEncryptionInfo, "ImsiEncryptionInfo cannot be null.");
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (simProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION, result,
-                    mRILDefaultWorkSource);
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                simProxy.setCarrierInfoForImsiEncryption(rr.mSerial, imsiEncryptionInfo);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM,
-                        "setCarrierInfoForImsiEncryption", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "setCarrierInfoForImsiEncryption: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("setCarrierInfoForImsiEncryption", simProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION, result,
+                mRILDefaultWorkSource);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "setCarrierInfoForImsiEncryption", () -> {
+            simProxy.setCarrierInfoForImsiEncryption(rr.mSerial, imsiEncryptionInfo);
+        });
     }
 
     @Override
     public void startNattKeepalive(int contextId, KeepalivePacketData packetData,
             int intervalMillis, Message result) {
         Objects.requireNonNull(packetData, "KeepaliveRequest cannot be null.");
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (dataProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_START_KEEPALIVE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                dataProxy.startKeepalive(rr.mSerial, contextId, packetData, intervalMillis, result);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "startNattKeepalive", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startNattKeepalive: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("startNattKeepalive", dataProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_START_KEEPALIVE, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "startNattKeepalive", () -> {
+            dataProxy.startKeepalive(rr.mSerial, contextId, packetData, intervalMillis, result);
+        });
     }
 
     @Override
     public void stopNattKeepalive(int sessionHandle, Message result) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (dataProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_STOP_KEEPALIVE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                dataProxy.stopKeepalive(rr.mSerial, sessionHandle);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "stopNattKeepalive", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopNattKeepalive: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("stopNattKeepalive", dataProxy, result, RADIO_HAL_VERSION_1_4)) {
+            return;
         }
-    }
 
-    @Override
-    public void getIMEI(Message result) {
-        throw new RuntimeException("getIMEI not expected to be called");
-    }
+        RILRequest rr = obtainRequest(RIL_REQUEST_STOP_KEEPALIVE, result, mRILDefaultWorkSource);
 
-    @Override
-    public void getIMEISV(Message result) {
-        throw new RuntimeException("getIMEISV not expected to be called");
-    }
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
 
-    /**
-     * @deprecated
-     */
-    @Deprecated
-    @Override
-    public void getLastPdpFailCause(Message result) {
-        throw new RuntimeException("getLastPdpFailCause not expected to be called");
-    }
-
-    /**
-     * The preferred new alternative to getLastPdpFailCause
-     */
-    @Override
-    public void getLastDataCallFailCause(Message result) {
-        throw new RuntimeException("getLastDataCallFailCause not expected to be called");
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "stopNattKeepalive", () -> {
+            dataProxy.stopKeepalive(rr.mSerial, sessionHandle);
+        });
     }
 
     /**
@@ -4856,30 +4460,22 @@
      */
     @Override
     public void enableUiccApplications(boolean enable, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (simProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_UICC_APPLICATIONS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " " + enable);
-            }
-
-            try {
-                simProxy.enableUiccApplications(rr.mSerial, enable);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "enableUiccApplications", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "enableUiccApplications: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("enableUiccApplications", simProxy, result, RADIO_HAL_VERSION_1_5)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_UICC_APPLICATIONS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " " + enable);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "enableUiccApplications", () -> {
+            simProxy.enableUiccApplications(rr.mSerial, enable);
+        });
     }
 
     /**
@@ -4889,31 +4485,22 @@
      */
     @Override
     public void areUiccApplicationsEnabled(Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (simProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                simProxy.areUiccApplicationsEnabled(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "areUiccApplicationsEnabled", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "areUiccApplicationsEnabled: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("areUiccApplicationsEnabled", simProxy, result,
+                RADIO_HAL_VERSION_1_5)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "areUiccApplicationsEnabled", () -> {
+            simProxy.areUiccApplicationsEnabled(rr.mSerial);
+        });
     }
 
     /**
@@ -4921,13 +4508,8 @@
      */
     @Override
     public boolean canToggleUiccApplicationsEnablement() {
-        return !getRadioServiceProxy(RadioSimProxy.class, null).isEmpty()
-                && mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5);
-    }
-
-    @Override
-    public void resetRadio(Message result) {
-        throw new RuntimeException("resetRadio not expected to be called");
+        return canMakeRequest("canToggleUiccApplicationsEnablement",
+                getRadioServiceProxy(RadioSimProxy.class), null, RADIO_HAL_VERSION_1_5);
     }
 
     /**
@@ -4935,22 +4517,22 @@
      */
     @Override
     public void handleCallSetupRequestFromSim(boolean accept, Message result) {
-        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
-        if (!voiceProxy.isEmpty()) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM,
-                    result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                voiceProxy.handleStkCallSetupRequestFromSim(rr.mSerial, accept);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(
-                        HAL_SERVICE_VOICE, "handleStkCallSetupRequestFromSim", e);
-            }
+        RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class);
+        if (!canMakeRequest("handleCallSetupRequestFromSim", voiceProxy, result,
+                RADIO_HAL_VERSION_1_4)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM,
+                result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_VOICE, rr, "handleCallSetupRequestFromSim", () -> {
+            voiceProxy.handleStkCallSetupRequestFromSim(rr.mSerial, accept);
+        });
     }
 
     /**
@@ -4958,29 +4540,20 @@
      */
     @Override
     public void getBarringInfo(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_BARRING_INFO, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getBarringInfo(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getBarringInfo", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "getBarringInfo: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getBarringInfo", networkProxy, result, RADIO_HAL_VERSION_1_5)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_BARRING_INFO, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getBarringInfo", () -> {
+            networkProxy.getBarringInfo(rr.mSerial);
+        });
     }
 
     /**
@@ -4988,25 +4561,20 @@
      */
     @Override
     public void allocatePduSessionId(Message result) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (dataProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_ALLOCATE_PDU_SESSION_ID, result,
-                    mRILDefaultWorkSource);
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                dataProxy.allocatePduSessionId(rr.mSerial);
-            } catch (RemoteException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "allocatePduSessionId", e);
-            }
-        } else {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("allocatePduSessionId", dataProxy, result, RADIO_HAL_VERSION_1_6)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ALLOCATE_PDU_SESSION_ID, result,
+                mRILDefaultWorkSource);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "allocatePduSessionId", () -> {
+            dataProxy.allocatePduSessionId(rr.mSerial);
+        });
     }
 
     /**
@@ -5014,25 +4582,20 @@
      */
     @Override
     public void releasePduSessionId(Message result, int pduSessionId) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (dataProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_RELEASE_PDU_SESSION_ID, result,
-                    mRILDefaultWorkSource);
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                dataProxy.releasePduSessionId(rr.mSerial, pduSessionId);
-            } catch (RemoteException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "releasePduSessionId", e);
-            }
-        } else {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("releasePduSessionId", dataProxy, result, RADIO_HAL_VERSION_1_6)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_RELEASE_PDU_SESSION_ID, result,
+                mRILDefaultWorkSource);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "releasePduSessionId", () -> {
+            dataProxy.releasePduSessionId(rr.mSerial, pduSessionId);
+        });
     }
 
     /**
@@ -5040,28 +4603,19 @@
      */
     @Override
     public void startHandover(Message result, int callId) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (dataProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_START_HANDOVER, result,
-                    mRILDefaultWorkSource);
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                dataProxy.startHandover(rr.mSerial, callId);
-            } catch (RemoteException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "startHandover", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startHandover: REQUEST_NOT_SUPPORTED");
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("startHandover", dataProxy, result, RADIO_HAL_VERSION_1_6)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_START_HANDOVER, result, mRILDefaultWorkSource);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "startHandover", () -> {
+            dataProxy.startHandover(rr.mSerial, callId);
+        });
     }
 
     /**
@@ -5069,26 +4623,19 @@
      */
     @Override
     public void cancelHandover(Message result, int callId) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (dataProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_HANDOVER, result,
-                    mRILDefaultWorkSource);
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                dataProxy.cancelHandover(rr.mSerial, callId);
-            } catch (RemoteException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "cancelHandover", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "cancelHandover: REQUEST_NOT_SUPPORTED");
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("cancelHandover", dataProxy, result, RADIO_HAL_VERSION_1_6)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_HANDOVER, result, mRILDefaultWorkSource);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "cancelHandover", () -> {
+            dataProxy.cancelHandover(rr.mSerial, callId);
+        });
     }
 
     /**
@@ -5096,115 +4643,79 @@
      */
     @Override
     public void getSlicingConfig(Message result) {
-        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
-        if (dataProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLICING_CONFIG, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                dataProxy.getSlicingConfig(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "getSlicingConfig", e);
-            }
-        } else {
-            if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "getSlicingConfig: REQUEST_NOT_SUPPORTED");
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class);
+        if (!canMakeRequest("getSlicingConfig", dataProxy, result, RADIO_HAL_VERSION_1_6)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLICING_CONFIG, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_DATA, rr, "getSlicingConfig", () -> {
+            dataProxy.getSlicingConfig(rr.mSerial);
+        });
     }
 
     @Override
     public void getSimPhonebookRecords(Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (simProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                simProxy.getSimPhonebookRecords(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getSimPhonebookRecords", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "getSimPhonebookRecords: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("getSimPhonebookRecords", simProxy, result, RADIO_HAL_VERSION_1_6)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "getSimPhonebookRecords", () -> {
+            simProxy.getSimPhonebookRecords(rr.mSerial);
+        });
     }
 
     @Override
     public void getSimPhonebookCapacity(Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (simProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                simProxy.getSimPhonebookCapacity(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getSimPhonebookCapacity", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "getSimPhonebookCapacity: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("getSimPhonebookCapacity", simProxy, result, RADIO_HAL_VERSION_1_6)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "getSimPhonebookCapacity", () -> {
+            simProxy.getSimPhonebookCapacity(rr.mSerial);
+        });
     }
 
     @Override
     public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
-        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
-        if (simProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " with " + phonebookRecord.toString());
-            }
-
-            try {
-                simProxy.updateSimPhonebookRecords(rr.mSerial, phonebookRecord);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "updateSimPhonebookRecords", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "updateSimPhonebookRecords: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
+        if (!canMakeRequest("updateSimPhonebookRecord", simProxy, result, RADIO_HAL_VERSION_1_6)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " with " + phonebookRecord.toString());
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "updateSimPhonebookRecord", () -> {
+            simProxy.updateSimPhonebookRecords(rr.mSerial, phonebookRecord);
+        });
     }
 
     /**
@@ -5216,31 +4727,20 @@
     @Override
     public void setUsageSetting(Message result,
             /* @TelephonyManager.UsageSetting */ int usageSetting) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_USAGE_SETTING, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.setUsageSetting(rr.mSerial, usageSetting);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setUsageSetting", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "setUsageSetting: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setUsageSetting", networkProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_USAGE_SETTING, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setUsageSetting", () -> {
+            networkProxy.setUsageSetting(rr.mSerial, usageSetting);
+        });
     }
 
     /**
@@ -5250,62 +4750,40 @@
      */
     @Override
     public void getUsageSetting(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_GET_USAGE_SETTING, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.getUsageSetting(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getUsageSetting", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "getUsageSetting: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("getUsageSetting", networkProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_USAGE_SETTING, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "getUsageSetting", () -> {
+            networkProxy.getUsageSetting(rr.mSerial);
+        });
     }
 
     @Override
     public void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {
-        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
-        if (imsProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SRVCC_CALL_INFO, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                // Do not log function arg for privacy
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                imsProxy.setSrvccCallInfo(rr.mSerial,
-                        RILUtils.convertToHalSrvccCall(srvccConnections));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "setSrvccCallInfo", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "setSrvccCallInfo: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class);
+        if (!canMakeRequest("setSrvccCallInfo", imsProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SRVCC_CALL_INFO, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_IMS, rr, "setSrvccCallInfo", () -> {
+            imsProxy.setSrvccCallInfo(rr.mSerial, RILUtils.convertToHalSrvccCall(srvccConnections));
+        });
     }
 
     @Override
@@ -5314,164 +4792,109 @@
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
             @RegistrationManager.SuggestedAction int suggestedAction,
             int capabilities, Message result) {
-        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
-        if (imsProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " state=" + state + ", radioTech=" + imsRadioTech
-                        + ", suggested=" + suggestedAction + ", cap=" + capabilities);
-            }
-
-            android.hardware.radio.ims.ImsRegistration registrationInfo =
-                    new android.hardware.radio.ims.ImsRegistration();
-            registrationInfo.regState = RILUtils.convertImsRegistrationState(state);
-            registrationInfo.accessNetworkType = RILUtils.convertImsRegistrationTech(imsRadioTech);
-            registrationInfo.suggestedAction = suggestedAction;
-            registrationInfo.capabilities = RILUtils.convertImsCapability(capabilities);
-
-            try {
-                imsProxy.updateImsRegistrationInfo(rr.mSerial, registrationInfo);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "updateImsRegistrationInfo", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "updateImsRegistrationInfo: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class);
+        if (!canMakeRequest("updateImsRegistrationInfo", imsProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " state=" + state + ", radioTech=" + imsRadioTech
+                    + ", suggested=" + suggestedAction + ", cap=" + capabilities);
+        }
+
+        android.hardware.radio.ims.ImsRegistration registrationInfo =
+                new android.hardware.radio.ims.ImsRegistration();
+        registrationInfo.regState = RILUtils.convertImsRegistrationState(state);
+        registrationInfo.accessNetworkType = RILUtils.convertImsRegistrationTech(imsRadioTech);
+        registrationInfo.suggestedAction = suggestedAction;
+        registrationInfo.capabilities = RILUtils.convertImsCapability(capabilities);
+
+        radioServiceInvokeHelper(HAL_SERVICE_IMS, rr, "updateImsRegistrationInfo", () -> {
+            imsProxy.updateImsRegistrationInfo(rr.mSerial, registrationInfo);
+        });
     }
 
     @Override
-    public void startImsTraffic(int token,
-            int trafficType, int accessNetworkType, int trafficDirection, Message result) {
-        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
-        if (imsProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_START_IMS_TRAFFIC, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + "{" + token + ", " + trafficType + ", "
-                        + accessNetworkType + ", " + trafficDirection + "}");
-            }
-
-            try {
-                imsProxy.startImsTraffic(rr.mSerial, token,
-                        RILUtils.convertImsTrafficType(trafficType), accessNetworkType,
-                        RILUtils.convertImsTrafficDirection(trafficDirection));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "startImsTraffic", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "startImsTraffic: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+    public void startImsTraffic(int token, int trafficType, int accessNetworkType,
+            int trafficDirection, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class);
+        if (!canMakeRequest("startImsTraffic", imsProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_START_IMS_TRAFFIC, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + "{" + token + ", " + trafficType + ", "
+                    + accessNetworkType + ", " + trafficDirection + "}");
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_IMS, rr, "startImsTraffic", () -> {
+            imsProxy.startImsTraffic(rr.mSerial, token, RILUtils.convertImsTrafficType(trafficType),
+                    accessNetworkType, RILUtils.convertImsTrafficDirection(trafficDirection));
+        });
     }
 
     @Override
     public void stopImsTraffic(int token, Message result) {
-        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
-        if (imsProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_STOP_IMS_TRAFFIC, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + "{" + token + "}");
-            }
-
-            try {
-                imsProxy.stopImsTraffic(rr.mSerial, token);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "stopImsTraffic", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "stopImsTraffic: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class);
+        if (!canMakeRequest("stopImsTraffic", imsProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_STOP_IMS_TRAFFIC, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + "{" + token + "}");
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_IMS, rr, "stopImsTraffic", () -> {
+            imsProxy.stopImsTraffic(rr.mSerial, token);
+        });
     }
 
     @Override
     public void triggerEpsFallback(int reason, Message result) {
-        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
-        if (imsProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_TRIGGER_EPS_FALLBACK, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " reason=" + reason);
-            }
-
-            try {
-                imsProxy.triggerEpsFallback(rr.mSerial, reason);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "triggerEpsFallback", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "triggerEpsFallback: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class);
+        if (!canMakeRequest("triggerEpsFallback", imsProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_TRIGGER_EPS_FALLBACK, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " reason=" + reason);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_IMS, rr, "triggerEpsFallback", () -> {
+            imsProxy.triggerEpsFallback(rr.mSerial, reason);
+        });
     }
 
     @Override
-    public void sendAnbrQuery(int mediaType, int direction, int bitsPerSecond,
-            Message result) {
-        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
-        if (imsProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SEND_ANBR_QUERY, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                imsProxy.sendAnbrQuery(rr.mSerial, mediaType, direction, bitsPerSecond);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "sendAnbrQuery", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "sendAnbrQuery: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+    public void sendAnbrQuery(int mediaType, int direction, int bitsPerSecond, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class);
+        if (!canMakeRequest("sendAnbrQuery", imsProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SEND_ANBR_QUERY, result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_IMS, rr, "sendAnbrQuery", () -> {
+            imsProxy.sendAnbrQuery(rr.mSerial, mediaType, direction, bitsPerSecond);
+        });
     }
 
     /**
@@ -5479,32 +4902,22 @@
      */
     @Override
     public void setEmergencyMode(int emcMode, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_EMERGENCY_MODE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " mode=" + EmergencyConstants.emergencyModeToString(emcMode));
-            }
-
-            try {
-                networkProxy.setEmergencyMode(rr.mSerial, emcMode);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setEmergencyMode", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "setEmergencyMode: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setEmergencyMode", networkProxy, result, RADIO_HAL_VERSION_2_1)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_EMERGENCY_MODE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " mode=" + EmergencyConstants.emergencyModeToString(emcMode));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setEmergencyMode", () -> {
+            networkProxy.setEmergencyMode(rr.mSerial, emcMode);
+        });
     }
 
     /**
@@ -5514,35 +4927,25 @@
     public void triggerEmergencyNetworkScan(
             @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork,
             @DomainSelectionService.EmergencyScanType int scanType, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " networkType=" + RILUtils.accessNetworkTypesToString(accessNetwork)
-                        + ", scanType=" + RILUtils.scanTypeToString(scanType));
-            }
-
-            try {
-                networkProxy.triggerEmergencyNetworkScan(rr.mSerial,
-                        RILUtils.convertEmergencyNetworkScanTrigger(accessNetwork, scanType));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "triggerEmergencyNetworkScan", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "triggerEmergencyNetworkScan: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("triggerEmergencyNetworkScan", networkProxy, result,
+                RADIO_HAL_VERSION_2_1)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " networkType=" + RILUtils.accessNetworkTypesToString(accessNetwork)
+                    + ", scanType=" + RILUtils.scanTypeToString(scanType));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "triggerEmergencyNetworkScan", () -> {
+            networkProxy.triggerEmergencyNetworkScan(rr.mSerial,
+                    RILUtils.convertEmergencyNetworkScanTrigger(accessNetwork, scanType));
+        });
     }
 
     /**
@@ -5550,33 +4953,23 @@
      */
     @Override
     public void cancelEmergencyNetworkScan(boolean resetScan, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " resetScan=" + resetScan);
-            }
-
-            try {
-                networkProxy.cancelEmergencyNetworkScan(rr.mSerial, resetScan);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
-                        "cancelEmergencyNetworkScan", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "cancelEmergencyNetworkScan: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("cancelEmergencyNetworkScan", networkProxy, result,
+                RADIO_HAL_VERSION_2_1)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " resetScan=" + resetScan);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "cancelEmergencyNetworkScan", () -> {
+            networkProxy.cancelEmergencyNetworkScan(rr.mSerial, resetScan);
+        });
     }
 
     /**
@@ -5584,31 +4977,21 @@
      */
     @Override
     public void exitEmergencyMode(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_EXIT_EMERGENCY_MODE, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.exitEmergencyMode(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "exitEmergencyMode", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "exitEmergencyMode: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("exitEmergencyMode", networkProxy, result, RADIO_HAL_VERSION_2_1)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_EXIT_EMERGENCY_MODE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "exitEmergencyMode", () -> {
+            networkProxy.exitEmergencyMode(rr.mSerial);
+        });
     }
 
     /**
@@ -5619,32 +5002,23 @@
      */
     @Override
     public void setNullCipherAndIntegrityEnabled(boolean enabled, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.setNullCipherAndIntegrityEnabled(rr.mSerial, enabled);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(
-                        HAL_SERVICE_NETWORK, "setNullCipherAndIntegrityEnabled", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "setNullCipherAndIntegrityEnabled: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setNullCipherAndIntegrityEnabled", networkProxy, result,
+                RADIO_HAL_VERSION_2_1)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setNullCipherAndIntegrityEnabled",
+                () -> {
+                    networkProxy.setNullCipherAndIntegrityEnabled(rr.mSerial, enabled);
+                });
     }
 
     /**
@@ -5654,32 +5028,22 @@
      */
     @Override
     public void isNullCipherAndIntegrityEnabled(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.isNullCipherAndIntegrityEnabled(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(
-                        HAL_SERVICE_NETWORK, "isNullCipherAndIntegrityEnabled", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "isNullCipherAndIntegrityEnabled: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("isNullCipherAndIntegrityEnabled", networkProxy, result,
+                RADIO_HAL_VERSION_2_1)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "isNullCipherAndIntegrityEnabled", () -> {
+            networkProxy.isNullCipherAndIntegrityEnabled(rr.mSerial);
+        });
     }
 
     /**
@@ -5687,31 +5051,21 @@
      */
     @Override
     public void updateImsCallStatus(@NonNull List<ImsCallInfo> imsCallInfo, Message result) {
-        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
-        if (imsProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_IMS_CALL_STATUS, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " " + imsCallInfo);
-            }
-            try {
-                imsProxy.updateImsCallStatus(rr.mSerial, RILUtils.convertImsCallInfo(imsCallInfo));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "updateImsCallStatus", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "updateImsCallStatus: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class);
+        if (!canMakeRequest("updateImsCallStatus", imsProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_IMS_CALL_STATUS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " " + imsCallInfo);
+        }
+        radioServiceInvokeHelper(HAL_SERVICE_IMS, rr, "updateImsCallStatus", () -> {
+            imsProxy.updateImsCallStatus(rr.mSerial, RILUtils.convertImsCallInfo(imsCallInfo));
+        });
     }
 
     /**
@@ -5719,32 +5073,22 @@
      */
     @Override
     public void setN1ModeEnabled(boolean enable, Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_N1_MODE_ENABLED, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
-                        + " enable=" + enable);
-            }
-
-            try {
-                networkProxy.setN1ModeEnabled(rr.mSerial, enable);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setN1ModeEnabled", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "setN1ModeEnabled: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("setN1ModeEnabled", networkProxy, result, RADIO_HAL_VERSION_2_1)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_N1_MODE_ENABLED, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " enable=" + enable);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setN1ModeEnabled", () -> {
+            networkProxy.setN1ModeEnabled(rr.mSerial, enable);
+        });
     }
 
     /**
@@ -5752,31 +5096,21 @@
      */
     @Override
     public void isN1ModeEnabled(Message result) {
-        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
-        if (networkProxy.isEmpty()) return;
-        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_IS_N1_MODE_ENABLED, result,
-                    mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
-            }
-
-            try {
-                networkProxy.isN1ModeEnabled(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "isN1ModeEnabled", e);
-            }
-        } else {
-            if (RILJ_LOGD) {
-                Rlog.d(RILJ_LOG_TAG, "isN1ModeEnabled: REQUEST_NOT_SUPPORTED");
-            }
-            if (result != null) {
-                AsyncResult.forMessage(result, null,
-                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                result.sendToTarget();
-            }
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest("isN1ModeEnabled", networkProxy, result, RADIO_HAL_VERSION_2_1)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_IS_N1_MODE_ENABLED, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "isN1ModeEnabled", () -> {
+            networkProxy.isN1ModeEnabled(rr.mSerial);
+        });
     }
 
     /**
@@ -5786,12 +5120,23 @@
      */
     @Override
     public void getSatelliteCapabilities(Message result) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("getSatelliteCapabilities", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_SATELLITE_CAPABILITIES, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "getSatelliteCapabilities", () -> {
+            satelliteProxy.getCapabilities(rr.mSerial);
+        });
     }
 
     /**
@@ -5803,12 +5148,22 @@
      */
     @Override
     public void setSatellitePower(Message result, boolean on) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("setSatellitePower", satelliteProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SATELLITE_POWER, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "setSatellitePower", () -> {
+            satelliteProxy.setPower(rr.mSerial, on);
+        });
     }
 
     /**
@@ -5818,12 +5173,23 @@
      */
     @Override
     public void getSatellitePowerState(Message result) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("getSatellitePowerState", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_SATELLITE_POWER, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "getSatellitePowerState", () -> {
+            satelliteProxy.getPowerState(rr.mSerial);
+        });
     }
 
     /**
@@ -5834,10 +5200,10 @@
     @Override
     public void getSatelliteProvisionState(Message result) {
         // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("getSatelliteProvisionState", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
     }
 
@@ -5854,12 +5220,23 @@
     @Override
     public void provisionSatelliteService(
             Message result, String imei, String msisdn, String imsi, int[] features) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("provisionSatelliteService", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_PROVISION_SATELLITE_SERVICE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "provisionSatelliteService", () -> {
+            satelliteProxy.provisionService(rr.mSerial, imei, msisdn, imsi, features);
+        });
     }
 
     /**
@@ -5871,12 +5248,23 @@
      */
     @Override
     public void addAllowedSatelliteContacts(Message result, String[] contacts) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("addAllowedSatelliteContacts", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ADD_ALLOWED_SATELLITE_CONTACTS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "addAllowedSatelliteContacts", () -> {
+            satelliteProxy.addAllowedSatelliteContacts(rr.mSerial, contacts);
+        });
     }
 
     /**
@@ -5888,12 +5276,24 @@
      */
     @Override
     public void removeAllowedSatelliteContacts(Message result, String[] contacts) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("removeAllowedSatelliteContacts", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_REMOVE_ALLOWED_SATELLITE_CONTACTS, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "removeAllowedSatelliteContacts",
+                () -> {
+                    satelliteProxy.removeAllowedSatelliteContacts(rr.mSerial, contacts);
+                });
     }
 
     /**
@@ -5908,12 +5308,23 @@
     @Override
     public void sendSatelliteMessages(Message result, String[] messages, String destination,
             double latitude, double longitude) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("sendSatelliteMessages", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SEND_SATELLITE_MESSAGES, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "sendSatelliteMessages", () -> {
+            satelliteProxy.sendMessages(rr.mSerial, messages, destination, latitude, longitude);
+        });
     }
 
     /**
@@ -5923,12 +5334,23 @@
      */
     @Override
     public void getPendingSatelliteMessages(Message result) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("getPendingSatelliteMessages", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_PENDING_SATELLITE_MESSAGES, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "getPendingSatelliteMessages", () -> {
+            satelliteProxy.getPendingMessages(rr.mSerial);
+        });
     }
 
     /**
@@ -5938,12 +5360,22 @@
      */
     @Override
     public void getSatelliteMode(Message result) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("getSatelliteMode", satelliteProxy, result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_SATELLITE_MODE, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "getSatelliteMode", () -> {
+            satelliteProxy.getSatelliteMode(rr.mSerial);
+        });
     }
 
     /**
@@ -5955,12 +5387,23 @@
      */
     @Override
     public void setSatelliteIndicationFilter(Message result, int filterBitmask) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("setSatelliteIndicationFilter", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SATELLITE_INDICATION_FILTER, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "setSatelliteIndicationFilter", () -> {
+            satelliteProxy.setIndicationFilter(rr.mSerial, filterBitmask);
+        });
     }
 
     /**
@@ -5970,12 +5413,16 @@
      */
     @Override
     public void isSatelliteSupported(Message result) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("isSatelliteSupported", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+        /**
+         * TODO: when adding implementation of this method, we need to return successful result
+         * with satellite support set to false if radioSatelliteProxy.isEmpty() is true or
+         * mHalVersion.get(HAL_SERVICE_SATELLITE).greaterOrEqual(RADIO_HAL_VERSION_2_0) is false.
+         */
     }
 
     /**
@@ -5986,12 +5433,24 @@
      */
     @Override
     public void startSendingSatellitePointingInfo(Message result) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("startSendingSatellitePointingInfo", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_START_SENDING_SATELLITE_POINTING_INFO, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "startSendingSatellitePointingInfo",
+                () -> {
+                    satelliteProxy.startSendingSatellitePointingInfo(rr.mSerial);
+                });
     }
 
     /**
@@ -6001,12 +5460,24 @@
      */
     @Override
     public void stopSendingSatellitePointingInfo(Message result) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("stopSendingSatellitePointingInfo", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_STOP_SENDING_SATELLITE_POINTING_INFO, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "stopSendingSatellitePointingInfo",
+                () -> {
+                    satelliteProxy.stopSendingSatellitePointingInfo(rr.mSerial);
+                });
     }
 
     /**
@@ -6016,12 +5487,24 @@
      */
     @Override
     public void getMaxCharactersPerSatelliteTextMessage(Message result) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("getMaxCharactersPerSatelliteTextMessage", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_MAX_CHARACTERS_PER_SATELLITE_TEXT_MESSAGE,
+                result, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr,
+                "getMaxCharactersPerSatelliteTextMessage", () -> {
+                    satelliteProxy.getMaxCharactersPerTextMessage(rr.mSerial);
+                });
     }
 
     /**
@@ -6031,11 +5514,11 @@
      */
     @Override
     public void isSatelliteCommunicationAllowedForCurrentLocation(Message result) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        // TODO: link to HAL implementation
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("isSatelliteCommunicationAllowedForCurrentLocation", satelliteProxy,
+                result, RADIO_HAL_VERSION_2_0)) {
+            return;
         }
     }
 
@@ -6046,12 +5529,24 @@
      */
     @Override
     public void getTimeForNextSatelliteVisibility(Message result) {
-        // Satellite HAL APIs are not supported before Android V.
-        if (result != null) {
-            AsyncResult.forMessage(result, null,
-                    CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-            result.sendToTarget();
+        RadioSatelliteProxy satelliteProxy = getRadioServiceProxy(RadioSatelliteProxy.class);
+        if (!canMakeRequest("getTimeForNextSatelliteVisibility", satelliteProxy, result,
+                RADIO_HAL_VERSION_2_0)) {
+            return;
         }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_TIME_FOR_NEXT_SATELLITE_VISIBILITY, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            // Do not log function arg for privacy
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_SATELLITE, rr, "getTimeForNextSatelliteVisibility",
+                () -> {
+                    satelliteProxy.getTimeForNextSatelliteVisibility(rr.mSerial);
+                });
     }
 
     //***** Private Methods
@@ -6076,8 +5571,7 @@
             rr = mRequestList.get(serial);
         }
         if (rr == null) {
-            Rlog.w(RILJ_LOG_TAG, "processRequestAck: Unexpected solicited ack response! "
-                    + "serial: " + serial);
+            riljLogw("processRequestAck: Unexpected solicited ack response! serial: " + serial);
         } else {
             decrementWakeLock(rr);
             if (RILJ_LOGD) {
@@ -6135,7 +5629,7 @@
                 rr = mRequestList.get(serial);
             }
             if (rr == null) {
-                Rlog.w(RILJ_LOG_TAG, "Unexpected solicited ack response! sn: " + serial);
+                riljLogw("Unexpected solicited ack response! sn: " + serial);
             } else {
                 decrementWakeLock(rr);
                 if (mRadioBugDetector != null) {
@@ -6151,8 +5645,8 @@
 
         rr = findAndRemoveRequestFromList(serial);
         if (rr == null) {
-            Rlog.e(RILJ_LOG_TAG, "processResponse: Unexpected response! serial: " + serial
-                    + " ,error: " + error);
+            riljLoge("processResponse: Unexpected response! serial: " + serial
+                    + ", error: " + error);
             return null;
         }
         Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_NETWORK, "RIL", rr.mSerial);
@@ -6321,7 +5815,7 @@
                 mRILDefaultWorkSource);
         acquireWakeLock(rr, FOR_ACK_WAKELOCK);
         if (service == HAL_SERVICE_RADIO) {
-            IRadio radioProxy = getRadioProxy(null);
+            IRadio radioProxy = getRadioProxy();
             if (radioProxy != null) {
                 try {
                     radioProxy.responseAcknowledgement();
@@ -6330,10 +5824,10 @@
                     riljLoge("sendAck: " + e);
                 }
             } else {
-                Rlog.e(RILJ_LOG_TAG, "Error trying to send ack, radioProxy = null");
+                riljLoge("Error trying to send ack, radioProxy = null");
             }
         } else {
-            RadioServiceProxy serviceProxy = getRadioServiceProxy(service, null);
+            RadioServiceProxy serviceProxy = getRadioServiceProxy(service);
             if (!serviceProxy.isEmpty()) {
                 try {
                     serviceProxy.responseAcknowledgement();
@@ -6342,7 +5836,7 @@
                     riljLoge("sendAck: " + e);
                 }
             } else {
-                Rlog.e(RILJ_LOG_TAG, "Error trying to send ack, serviceProxy is empty");
+                riljLoge("Error trying to send ack, serviceProxy is empty");
             }
         }
         rr.release();
@@ -6369,11 +5863,11 @@
     private void acquireWakeLock(RILRequest rr, int wakeLockType) {
         synchronized (rr) {
             if (rr.mWakeLockType != INVALID_WAKELOCK) {
-                Rlog.d(RILJ_LOG_TAG, "Failed to aquire wakelock for " + rr.serialString());
+                riljLog("Failed to acquire wakelock for " + rr.serialString());
                 return;
             }
 
-            switch(wakeLockType) {
+            switch (wakeLockType) {
                 case FOR_WAKELOCK:
                     synchronized (mWakeLock) {
                         mWakeLock.acquire();
@@ -6405,7 +5899,7 @@
                     }
                     break;
                 default: //WTF
-                    Rlog.w(RILJ_LOG_TAG, "Acquiring Invalid Wakelock type " + wakeLockType);
+                    riljLogw("Acquiring Invalid Wakelock type " + wakeLockType);
                     return;
             }
             rr.mWakeLockType = wakeLockType;
@@ -6459,7 +5953,7 @@
                 case INVALID_WAKELOCK:
                     break;
                 default:
-                    Rlog.w(RILJ_LOG_TAG, "Decrementing Invalid Wakelock type " + rr.mWakeLockType);
+                    riljLogw("Decrementing Invalid Wakelock type " + rr.mWakeLockType);
             }
             rr.mWakeLockType = INVALID_WAKELOCK;
         }
@@ -6470,8 +5964,7 @@
         if (wakeLockType == FOR_WAKELOCK) {
             synchronized (mWakeLock) {
                 if (mWakeLockCount == 0 && !mWakeLock.isHeld()) return false;
-                Rlog.d(RILJ_LOG_TAG, "NOTE: mWakeLockCount is " + mWakeLockCount
-                        + " at time of clearing");
+                riljLog("NOTE: mWakeLockCount is " + mWakeLockCount + " at time of clearing");
                 mWakeLockCount = 0;
                 mWakeLock.release();
                 mClientWakelockTracker.stopTrackingAll();
@@ -6498,15 +5991,14 @@
         synchronized (mRequestList) {
             int count = mRequestList.size();
             if (RILJ_LOGD && loggable) {
-                Rlog.d(RILJ_LOG_TAG, "clearRequestList " + " mWakeLockCount="
-                        + mWakeLockCount + " mRequestList=" + count);
+                riljLog("clearRequestList " + " mWakeLockCount=" + mWakeLockCount
+                        + " mRequestList=" + count);
             }
 
             for (int i = 0; i < count; i++) {
                 rr = mRequestList.valueAt(i);
                 if (RILJ_LOGD && loggable) {
-                    Rlog.d(RILJ_LOG_TAG, i + ": [" + rr.mSerial + "] "
-                            + RILUtils.requestToString(rr.mRequest));
+                    riljLog(i + ": [" + rr.mSerial + "] " + RILUtils.requestToString(rr.mRequest));
                 }
                 rr.onError(error, null);
                 decrementWakeLock(rr);
@@ -6776,18 +6268,22 @@
         Rlog.v(RILJ_LOG_TAG, msg + (" [PHONE" + mPhoneId + "]"));
     }
 
+    void riljLogw(String msg) {
+        Rlog.w(RILJ_LOG_TAG, msg + (" [PHONE" + mPhoneId + "]"));
+    }
+
     boolean isLogOrTrace() {
-        return RIL.RILJ_LOGD || Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK);
+        return RILJ_LOGD || Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK);
     }
 
     boolean isLogvOrTrace() {
-        return RIL.RILJ_LOGV || Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK);
+        return RILJ_LOGV || Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK);
     }
 
     @UnsupportedAppUsage
     void unsljLog(int response) {
         String logStr = RILUtils.responseToString(response);
-        if (RIL.RILJ_LOGD) {
+        if (RILJ_LOGD) {
             riljLog("[UNSL]< " + logStr);
         }
         Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr);
@@ -6796,7 +6292,7 @@
     @UnsupportedAppUsage
     void unsljLogMore(int response, String more) {
         String logStr = RILUtils.responseToString(response) + " " + more;
-        if (RIL.RILJ_LOGD) {
+        if (RILJ_LOGD) {
             riljLog("[UNSL]< " + logStr);
         }
         Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr);
@@ -6805,7 +6301,7 @@
     @UnsupportedAppUsage
     void unsljLogRet(int response, Object ret) {
         String logStr = RILUtils.responseToString(response) + " " + retToString(response, ret);
-        if (RIL.RILJ_LOGD) {
+        if (RILJ_LOGD) {
             riljLog("[UNSL]< " + logStr);
         }
         Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr);
@@ -6814,7 +6310,7 @@
     @UnsupportedAppUsage
     void unsljLogvRet(int response, Object ret) {
         String logStr = RILUtils.responseToString(response) + " " + retToString(response, ret);
-        if (RIL.RILJ_LOGV) {
+        if (RILJ_LOGV) {
             riljLogv("[UNSL]< " + logStr);
         }
         Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr);
@@ -6844,6 +6340,7 @@
         pw.println(" " + mServiceProxies.get(HAL_SERVICE_SIM));
         pw.println(" " + mServiceProxies.get(HAL_SERVICE_VOICE));
         pw.println(" " + mServiceProxies.get(HAL_SERVICE_IMS));
+        pw.println(" " + mServiceProxies.get(HAL_SERVICE_SATELLITE));
         pw.println(" mWakeLock=" + mWakeLock);
         pw.println(" mWakeLockTimeout=" + mWakeLockTimeout);
         synchronized (mRequestList) {
@@ -6867,58 +6364,6 @@
         return mClientWakelockTracker.getClientRequestStats();
     }
 
-    /**
-     * Fixup for SignalStrength 1.0 to Assume GSM to WCDMA when
-     * The current RAT type is one of the UMTS RATs.
-     * @param signalStrength the initial signal strength
-     * @return a new SignalStrength if RAT is UMTS or existing SignalStrength
-     */
-    public SignalStrength fixupSignalStrength10(SignalStrength signalStrength) {
-        List<CellSignalStrengthGsm> gsmList = signalStrength.getCellSignalStrengths(
-                CellSignalStrengthGsm.class);
-        // If GSM is not the primary type, then bail out; no fixup needed.
-        if (gsmList.isEmpty() || !gsmList.get(0).isValid()) {
-            return signalStrength;
-        }
-
-        CellSignalStrengthGsm gsmStrength = gsmList.get(0);
-
-        // Use the voice RAT which is a guarantee in GSM and UMTS
-        int voiceRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
-        Phone phone = PhoneFactory.getPhone(mPhoneId);
-        if (phone != null) {
-            ServiceState ss = phone.getServiceState();
-            if (ss != null) {
-                voiceRat = ss.getRilVoiceRadioTechnology();
-            }
-        }
-        switch (voiceRat) {
-            case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS: /* fallthrough */
-            case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA: /* fallthrough */
-            case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA: /* fallthrough */
-            case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA: /* fallthrough */
-            case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP: /* fallthrough */
-                break;
-            default:
-                // If we are not currently on WCDMA/HSPA, then we don't need to do a fixup.
-                return signalStrength;
-        }
-
-        // The service state reports WCDMA, and the SignalStrength is reported for GSM, so at this
-        // point we take an educated guess that the GSM SignalStrength report is actually for
-        // WCDMA. Also, if we are in WCDMA/GSM we can safely assume that there are no other valid
-        // signal strength reports (no SRLTE, which is the only supported case in HAL 1.0).
-        // Thus, we just construct a new SignalStrength and migrate RSSI and BER from the
-        // GSM report to the WCDMA report, leaving everything else empty.
-        return new SignalStrength(
-                new CellSignalStrengthCdma(), new CellSignalStrengthGsm(),
-                new CellSignalStrengthWcdma(gsmStrength.getRssi(),
-                        gsmStrength.getBitErrorRate(),
-                        CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE),
-                new CellSignalStrengthTdscdma(), new CellSignalStrengthLte(),
-                new CellSignalStrengthNr());
-    }
-
     void notifyBarringInfoChanged(@NonNull BarringInfo barringInfo) {
         mLastBarringInfo = barringInfo;
         mBarringInfoChangedRegistrants.notifyRegistrants(new AsyncResult(null, barringInfo, null));
@@ -6951,6 +6396,7 @@
         switch (interfaceVersion) {
             case 1: return RADIO_HAL_VERSION_2_0;
             case 2: return RADIO_HAL_VERSION_2_1;
+            case 3: return RADIO_HAL_VERSION_2_2;
             default: return RADIO_HAL_VERSION_UNKNOWN;
         }
     }
@@ -6973,6 +6419,8 @@
                 return "VOICE";
             case HAL_SERVICE_IMS:
                 return "IMS";
+            case HAL_SERVICE_SATELLITE:
+                return "SATELLITE";
             default:
                 return "UNKNOWN:" + service;
         }
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index ae8d033..d41b725 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -25,6 +25,7 @@
 import static android.telephony.TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK;
 
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ADD_ALLOWED_SATELLITE_CONTACTS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ALLOCATE_PDU_SESSION_ID;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ALLOW_DATA;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
@@ -91,12 +92,18 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_IMEI;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_IMEISV;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_IMSI;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_LOCATION_PRIVACY_SETTING;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_MAX_CHARACTERS_PER_SATELLITE_TEXT_MESSAGE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_MODEM_STATUS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_MUTE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_NEIGHBORING_CELL_IDS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_PENDING_SATELLITE_MESSAGES;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_PHONE_CAPABILITY;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_RADIO_CAPABILITY;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SATELLITE_CAPABILITIES;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SATELLITE_MODE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SATELLITE_POWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SIM_STATUS;
@@ -104,6 +111,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SLOT_STATUS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SMSC_ADDRESS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_TIME_FOR_NEXT_SATELLITE_VISIBILITY;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_USAGE_SETTING;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GSM_BROADCAST_ACTIVATION;
@@ -128,6 +136,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_OEM_HOOK_RAW;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_OEM_HOOK_STRINGS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_OPERATOR;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_PROVISION_SATELLITE_SERVICE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_PULL_LCEDATA;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_QUERY_AVAILABLE_NETWORKS;
@@ -139,12 +148,14 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_QUERY_TTY_MODE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RELEASE_PDU_SESSION_ID;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_REMOVE_ALLOWED_SATELLITE_CONTACTS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_REPORT_SMS_MEMORY_STATUS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RESET_RADIO;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SCREEN_STATE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_ANBR_QUERY;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_DEVICE_STATE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SATELLITE_MESSAGES;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_USSD;
@@ -164,6 +175,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_FACILITY_LOCK;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_INITIAL_ATTACH_APN;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOCATION_PRIVACY_SETTING;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOCATION_UPDATES;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_MUTE;
@@ -174,6 +186,8 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_DATA_MODEM;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_RADIO_CAPABILITY;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SATELLITE_INDICATION_FILTER;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SATELLITE_POWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIM_CARD_POWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SMSC_ADDRESS;
@@ -199,6 +213,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_KEEPALIVE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_LCE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_NETWORK_SCAN;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_SENDING_SATELLITE_POINTING_INFO;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_GET_PROFILE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND;
@@ -209,6 +224,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_KEEPALIVE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_LCE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_NETWORK_SCAN;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_SENDING_SATELLITE_POINTING_INFO;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN;
@@ -243,6 +259,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_LCEDATA_RECV;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_MODEM_RESTART;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NETWORK_SCAN_RESULT;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NEW_SATELLITE_MESSAGES;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NITZ_TIME_RECEIVED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NOTIFY_ANBR;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_OEM_HOOK_RAW;
@@ -250,6 +267,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_USSD;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_USSD_REQUEST;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PCO_DATA;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PENDING_SATELLITE_MESSAGE_COUNT;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RADIO_CAPABILITY;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_REGISTRATION_FAILED;
@@ -269,6 +287,11 @@
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESTRICTED_STATE_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RIL_CONNECTED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RINGBACK_TONE;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_MESSAGES_TRANSFER_COMPLETE;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_MODE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_POINTING_INFO_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_PROVISION_STATE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_RADIO_TECHNOLOGY_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIGNAL_STRENGTH;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIM_REFRESH;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIM_SMS_STORAGE_FULL;
@@ -357,6 +380,8 @@
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase.ImsDeregistrationReason;
+import android.telephony.satellite.PointingInfo;
+import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -874,41 +899,6 @@
     }
 
     /**
-     * Convert to DataProfileInfo defined in radio/1.0/types.hal
-     * @param dp Data profile
-     * @return The converted DataProfileInfo
-     */
-    public static android.hardware.radio.V1_0.DataProfileInfo convertToHalDataProfile10(
-            DataProfile dp) {
-        android.hardware.radio.V1_0.DataProfileInfo dpi =
-                new android.hardware.radio.V1_0.DataProfileInfo();
-
-        dpi.profileId = dp.getProfileId();
-        dpi.apn = dp.getApn();
-        dpi.protocol = ApnSetting.getProtocolStringFromInt(dp.getProtocolType());
-        dpi.roamingProtocol = ApnSetting.getProtocolStringFromInt(dp.getRoamingProtocolType());
-        dpi.authType = dp.getAuthType();
-        dpi.user = TextUtils.emptyIfNull(dp.getUserName());
-        dpi.password = TextUtils.emptyIfNull(dp.getPassword());
-        dpi.type = dp.getType();
-        dpi.maxConnsTime = dp.getMaxConnectionsTime();
-        dpi.maxConns = dp.getMaxConnections();
-        dpi.waitTime = dp.getWaitTime();
-        dpi.enabled = dp.isEnabled();
-        dpi.supportedApnTypesBitmap = dp.getSupportedApnTypesBitmask();
-        // Shift by 1 bit due to the discrepancy between
-        // android.hardware.radio.V1_0.RadioAccessFamily and the bitmask version of
-        // ServiceState.RIL_RADIO_TECHNOLOGY_XXXX.
-        dpi.bearerBitmap = ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
-                dp.getBearerBitmask()) << 1;
-        dpi.mtu = dp.getMtuV4();
-        dpi.mvnoType = android.hardware.radio.V1_0.MvnoType.NONE;
-        dpi.mvnoMatchData = "";
-
-        return dpi;
-    }
-
-    /**
      * Convert to DataProfileInfo defined in radio/1.4/types.hal
      * @param dp Data profile
      * @return The converted DataProfileInfo
@@ -2198,21 +2188,15 @@
     }
 
     /**
-     * Convert LceDataInfo defined in radio/1.0/types.hal and LinkCapacityEstimate defined in
-     * radio/1.2, 1.6/types.hal to a list of LinkCapacityEstimates
-     * @param lceObj LceDataInfo defined in radio/1.0/types.hal or LinkCapacityEstimate defined in
-     *        radio/1.2, 1.6/types.hal
+     * Convert LinkCapacityEstimate defined in radio/1.2, 1.6/types.hal to
+     * a list of LinkCapacityEstimates
+     * @param lceObj LinkCapacityEstimate defined in radio/1.2, 1.6/types.hal
      * @return The converted list of LinkCapacityEstimates
      */
-    public static List<LinkCapacityEstimate> convertHalLceData(Object lceObj) {
+    public static List<LinkCapacityEstimate> convertHalLinkCapacityEstimate(Object lceObj) {
         final List<LinkCapacityEstimate> lceList = new ArrayList<>();
         if (lceObj == null) return lceList;
-        if (lceObj instanceof android.hardware.radio.V1_0.LceDataInfo) {
-            android.hardware.radio.V1_0.LceDataInfo lce =
-                    (android.hardware.radio.V1_0.LceDataInfo) lceObj;
-            lceList.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_COMBINED,
-                    lce.lastHopCapacityKbps, LinkCapacityEstimate.INVALID));
-        } else if (lceObj instanceof android.hardware.radio.V1_2.LinkCapacityEstimate) {
+        if (lceObj instanceof android.hardware.radio.V1_2.LinkCapacityEstimate) {
             android.hardware.radio.V1_2.LinkCapacityEstimate lce =
                     (android.hardware.radio.V1_2.LinkCapacityEstimate) lceObj;
             lceList.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_COMBINED,
@@ -2241,25 +2225,12 @@
     }
 
     /**
-     * Convert LceDataInfo defined in LceDataInfo.aidl to a list of LinkCapacityEstimates
-     * @param lce LceDataInfo defined in LceDataInfo.aidl
-     * @return The converted list of LinkCapacityEstimates
-     */
-    public static List<LinkCapacityEstimate> convertHalLceData(
-            android.hardware.radio.network.LceDataInfo lce) {
-        final List<LinkCapacityEstimate> lceList = new ArrayList<>();
-        lceList.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_COMBINED,
-                lce.lastHopCapacityKbps, LinkCapacityEstimate.INVALID));
-        return lceList;
-    }
-
-    /**
      * Convert LinkCapacityEstimate defined in LinkCapacityEstimate.aidl to a list of
      * LinkCapacityEstimates
      * @param lce LinkCapacityEstimate defined in LinkCapacityEstimate.aidl
      * @return The converted list of LinkCapacityEstimates
      */
-    public static List<LinkCapacityEstimate> convertHalLceData(
+    public static List<LinkCapacityEstimate> convertHalLinkCapacityEstimate(
             android.hardware.radio.network.LinkCapacityEstimate lce) {
         final List<LinkCapacityEstimate> lceList = new ArrayList<>();
         int primaryDownlinkCapacityKbps = lce.downlinkCapacityKbps;
@@ -2283,9 +2254,9 @@
 
 
     /**
-     * Convert a list of CellInfo defined in radio/1.0, 1.2, 1.4, 1.5, 1.6/types.hal to a list of
+     * Convert a list of CellInfo defined in radio/1.4, 1.5, 1.6/types.hal to a list of
      * CellInfos
-     * @param records List of CellInfo defined in radio/1.0, 1.2, 1.4, 1.5, 1.6/types.hal
+     * @param records List of CellInfo defined in radio/1.4, 1.5, 1.6/types.hal
      * @return The converted list of CellInfos
      */
     public static ArrayList<CellInfo> convertHalCellInfoList(ArrayList<Object> records) {
@@ -2315,8 +2286,8 @@
     }
 
     /**
-     * Convert a CellInfo defined in radio/1.0, 1.2, 1.4, 1.5, 1.6/types.hal to CellInfo
-     * @param cellInfo CellInfo defined in radio/1.0, 1.2, 1.4, 1.5, 1.6/types.hal
+     * Convert a CellInfo defined in radio/1.4, 1.5, 1.6/types.hal to CellInfo
+     * @param cellInfo CellInfo defined in radio/1.4, 1.5, 1.6/types.hal
      * @param nanotime time the CellInfo was created
      * @return The converted CellInfo
      */
@@ -2338,87 +2309,7 @@
         CellSignalStrengthTdscdma tdscdmaSs = null;
         CellIdentityNr nrCi = null;
         CellSignalStrengthNr nrSs = null;
-        if (cellInfo instanceof android.hardware.radio.V1_0.CellInfo) {
-            final android.hardware.radio.V1_0.CellInfo record =
-                    (android.hardware.radio.V1_0.CellInfo) cellInfo;
-            connectionStatus = CellInfo.CONNECTION_UNKNOWN;
-            registered = record.registered;
-            switch (record.cellInfoType) {
-                case android.hardware.radio.V1_0.CellInfoType.GSM:
-                    type = CellInfo.TYPE_GSM;
-                    android.hardware.radio.V1_0.CellInfoGsm gsm = record.gsm.get(0);
-                    gsmCi = convertHalCellIdentityGsm(gsm.cellIdentityGsm);
-                    gsmSs = convertHalGsmSignalStrength(gsm.signalStrengthGsm);
-                    break;
-                case android.hardware.radio.V1_0.CellInfoType.CDMA:
-                    type = CellInfo.TYPE_CDMA;
-                    android.hardware.radio.V1_0.CellInfoCdma cdma = record.cdma.get(0);
-                    cdmaCi = convertHalCellIdentityCdma(cdma.cellIdentityCdma);
-                    cdmaSs = convertHalCdmaSignalStrength(
-                            cdma.signalStrengthCdma, cdma.signalStrengthEvdo);
-                    break;
-                case android.hardware.radio.V1_0.CellInfoType.LTE:
-                    type = CellInfo.TYPE_LTE;
-                    android.hardware.radio.V1_0.CellInfoLte lte = record.lte.get(0);
-                    lteCi = convertHalCellIdentityLte(lte.cellIdentityLte);
-                    lteSs = convertHalLteSignalStrength(lte.signalStrengthLte);
-                    lteCc = new CellConfigLte();
-                    break;
-                case android.hardware.radio.V1_0.CellInfoType.WCDMA:
-                    type = CellInfo.TYPE_WCDMA;
-                    android.hardware.radio.V1_0.CellInfoWcdma wcdma = record.wcdma.get(0);
-                    wcdmaCi = convertHalCellIdentityWcdma(wcdma.cellIdentityWcdma);
-                    wcdmaSs = convertHalWcdmaSignalStrength(wcdma.signalStrengthWcdma);
-                    break;
-                case android.hardware.radio.V1_0.CellInfoType.TD_SCDMA:
-                    type = CellInfo.TYPE_TDSCDMA;
-                    android.hardware.radio.V1_0.CellInfoTdscdma tdscdma = record.tdscdma.get(0);
-                    tdscdmaCi = convertHalCellIdentityTdscdma(tdscdma.cellIdentityTdscdma);
-                    tdscdmaSs = convertHalTdscdmaSignalStrength(tdscdma.signalStrengthTdscdma);
-                    break;
-                default: return null;
-            }
-        } else if (cellInfo instanceof android.hardware.radio.V1_2.CellInfo) {
-            final android.hardware.radio.V1_2.CellInfo record =
-                    (android.hardware.radio.V1_2.CellInfo) cellInfo;
-            connectionStatus = record.connectionStatus;
-            registered = record.registered;
-            switch(record.cellInfoType) {
-                case android.hardware.radio.V1_0.CellInfoType.GSM:
-                    type = CellInfo.TYPE_GSM;
-                    android.hardware.radio.V1_2.CellInfoGsm gsm = record.gsm.get(0);
-                    gsmCi = convertHalCellIdentityGsm(gsm.cellIdentityGsm);
-                    gsmSs = convertHalGsmSignalStrength(gsm.signalStrengthGsm);
-                    break;
-                case android.hardware.radio.V1_0.CellInfoType.CDMA:
-                    type = CellInfo.TYPE_CDMA;
-                    android.hardware.radio.V1_2.CellInfoCdma cdma = record.cdma.get(0);
-                    cdmaCi = convertHalCellIdentityCdma(cdma.cellIdentityCdma);
-                    cdmaSs = convertHalCdmaSignalStrength(
-                            cdma.signalStrengthCdma, cdma.signalStrengthEvdo);
-                    break;
-                case android.hardware.radio.V1_0.CellInfoType.LTE:
-                    type = CellInfo.TYPE_LTE;
-                    android.hardware.radio.V1_2.CellInfoLte lte = record.lte.get(0);
-                    lteCi = convertHalCellIdentityLte(lte.cellIdentityLte);
-                    lteSs = convertHalLteSignalStrength(lte.signalStrengthLte);
-                    lteCc = new CellConfigLte();
-                    break;
-                case android.hardware.radio.V1_0.CellInfoType.WCDMA:
-                    type = CellInfo.TYPE_WCDMA;
-                    android.hardware.radio.V1_2.CellInfoWcdma wcdma = record.wcdma.get(0);
-                    wcdmaCi = convertHalCellIdentityWcdma(wcdma.cellIdentityWcdma);
-                    wcdmaSs = convertHalWcdmaSignalStrength(wcdma.signalStrengthWcdma);
-                    break;
-                case android.hardware.radio.V1_0.CellInfoType.TD_SCDMA:
-                    type = CellInfo.TYPE_TDSCDMA;
-                    android.hardware.radio.V1_2.CellInfoTdscdma tdscdma = record.tdscdma.get(0);
-                    tdscdmaCi = convertHalCellIdentityTdscdma(tdscdma.cellIdentityTdscdma);
-                    tdscdmaSs = convertHalTdscdmaSignalStrength(tdscdma.signalStrengthTdscdma);
-                    break;
-                default: return null;
-            }
-        } else if (cellInfo instanceof android.hardware.radio.V1_4.CellInfo) {
+        if (cellInfo instanceof android.hardware.radio.V1_4.CellInfo) {
             final android.hardware.radio.V1_4.CellInfo record =
                     (android.hardware.radio.V1_4.CellInfo) cellInfo;
             connectionStatus = record.connectionStatus;
@@ -2648,43 +2539,13 @@
     }
 
     /**
-     * Convert a CellIdentity defined in radio/1.0, 1.2, 1.5/types.hal to CellIdentity
-     * @param halCi CellIdentity defined in radio/1.0, 1.2, 1.5/types.hal
+     * Convert a CellIdentity defined in radio/1.2, 1.5/types.hal to CellIdentity
+     * @param halCi CellIdentity defined in radio/1.2, 1.5/types.hal
      * @return The converted CellIdentity
      */
     public static CellIdentity convertHalCellIdentity(Object halCi) {
         if (halCi == null) return null;
-        if (halCi instanceof android.hardware.radio.V1_0.CellIdentity) {
-            android.hardware.radio.V1_0.CellIdentity ci =
-                    (android.hardware.radio.V1_0.CellIdentity) halCi;
-            switch (ci.cellInfoType) {
-                case CellInfo.TYPE_GSM:
-                    if (ci.cellIdentityGsm.size() == 1) {
-                        return convertHalCellIdentityGsm(ci.cellIdentityGsm.get(0));
-                    }
-                    break;
-                case CellInfo.TYPE_CDMA:
-                    if (ci.cellIdentityCdma.size() == 1) {
-                        return convertHalCellIdentityCdma(ci.cellIdentityCdma.get(0));
-                    }
-                    break;
-                case CellInfo.TYPE_LTE:
-                    if (ci.cellIdentityLte.size() == 1) {
-                        return convertHalCellIdentityLte(ci.cellIdentityLte.get(0));
-                    }
-                    break;
-                case CellInfo.TYPE_WCDMA:
-                    if (ci.cellIdentityWcdma.size() == 1) {
-                        return convertHalCellIdentityWcdma(ci.cellIdentityWcdma.get(0));
-                    }
-                    break;
-                case CellInfo.TYPE_TDSCDMA:
-                    if (ci.cellIdentityTdscdma.size() == 1) {
-                        return convertHalCellIdentityTdscdma(ci.cellIdentityTdscdma.get(0));
-                    }
-                    break;
-            }
-        } else if (halCi instanceof android.hardware.radio.V1_2.CellIdentity) {
+        if (halCi instanceof android.hardware.radio.V1_2.CellIdentity) {
             android.hardware.radio.V1_2.CellIdentity ci =
                     (android.hardware.radio.V1_2.CellIdentity) halCi;
             switch (ci.cellInfoType) {
@@ -2761,19 +2622,13 @@
     }
 
     /**
-     * Convert a CellIdentityGsm defined in radio/1.0, 1.2, 1.5/types.hal to CellIdentityGsm
-     * @param gsm CellIdentityGsm defined in radio/1.0, 1.2, 1.5/types.hal
+     * Convert a CellIdentityGsm defined in radio/1.2, 1.5/types.hal to CellIdentityGsm
+     * @param gsm CellIdentityGsm defined in radio/1.2, 1.5/types.hal
      * @return The converted CellIdentityGsm
      */
     public static CellIdentityGsm convertHalCellIdentityGsm(Object gsm) {
         if (gsm == null) return null;
-        if (gsm instanceof android.hardware.radio.V1_0.CellIdentityGsm) {
-            android.hardware.radio.V1_0.CellIdentityGsm ci =
-                    (android.hardware.radio.V1_0.CellIdentityGsm) gsm;
-            return new CellIdentityGsm(ci.lac, ci.cid, ci.arfcn,
-                    ci.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : ci.bsic, ci.mcc, ci.mnc, "", "",
-                    new ArraySet<>());
-        } else if (gsm instanceof android.hardware.radio.V1_2.CellIdentityGsm) {
+        if (gsm instanceof android.hardware.radio.V1_2.CellIdentityGsm) {
             android.hardware.radio.V1_2.CellIdentityGsm ci =
                     (android.hardware.radio.V1_2.CellIdentityGsm) gsm;
             return new CellIdentityGsm(ci.base.lac, ci.base.cid, ci.base.arfcn,
@@ -2806,18 +2661,13 @@
     }
 
     /**
-     * Convert a CellIdentityCdma defined in radio/1.0, 1.2/types.hal to CellIdentityCdma
-     * @param cdma CellIdentityCdma defined in radio/1.0, 1.2/types.hal
+     * Convert a CellIdentityCdma defined in radio/1.2/types.hal to CellIdentityCdma
+     * @param cdma CellIdentityCdma defined in radio/1.2/types.hal
      * @return The converted CellIdentityCdma
      */
     public static CellIdentityCdma convertHalCellIdentityCdma(Object cdma) {
         if (cdma == null) return null;
-        if (cdma instanceof android.hardware.radio.V1_0.CellIdentityCdma) {
-            android.hardware.radio.V1_0.CellIdentityCdma ci =
-                    (android.hardware.radio.V1_0.CellIdentityCdma) cdma;
-            return new CellIdentityCdma(ci.networkId, ci.systemId, ci.baseStationId, ci.longitude,
-                    ci.latitude, "", "");
-        } else if (cdma instanceof android.hardware.radio.V1_2.CellIdentityCdma) {
+        if (cdma instanceof android.hardware.radio.V1_2.CellIdentityCdma) {
             android.hardware.radio.V1_2.CellIdentityCdma ci =
                     (android.hardware.radio.V1_2.CellIdentityCdma) cdma;
             return new CellIdentityCdma(ci.base.networkId, ci.base.systemId, ci.base.baseStationId,
@@ -2840,18 +2690,13 @@
     }
 
     /**
-     * Convert a CellIdentityLte defined in radio/1.0, 1.2, 1.5/types.hal to CellIdentityLte
-     * @param lte CellIdentityLte defined in radio/1.0, 1.2, 1.5/types.hal
+     * Convert a CellIdentityLte defined in radio/1.2, 1.5/types.hal to CellIdentityLte
+     * @param lte CellIdentityLte defined in radio/1.2, 1.5/types.hal
      * @return The converted CellIdentityLte
      */
     public static CellIdentityLte convertHalCellIdentityLte(Object lte) {
         if (lte == null) return null;
-        if (lte instanceof android.hardware.radio.V1_0.CellIdentityLte) {
-            android.hardware.radio.V1_0.CellIdentityLte ci =
-                    (android.hardware.radio.V1_0.CellIdentityLte) lte;
-            return new CellIdentityLte(ci.ci, ci.pci, ci.tac, ci.earfcn, new int[] {},
-                    CellInfo.UNAVAILABLE, ci.mcc, ci.mnc, "", "", new ArraySet<>(), null);
-        } else if (lte instanceof android.hardware.radio.V1_2.CellIdentityLte) {
+        if (lte instanceof android.hardware.radio.V1_2.CellIdentityLte) {
             android.hardware.radio.V1_2.CellIdentityLte ci =
                     (android.hardware.radio.V1_2.CellIdentityLte) lte;
             return new CellIdentityLte(ci.base.ci, ci.base.pci, ci.base.tac, ci.base.earfcn,
@@ -2885,18 +2730,13 @@
     }
 
     /**
-     * Convert a CellIdentityWcdma defined in radio/1.0, 1.2, 1.5/types.hal to CellIdentityWcdma
-     * @param wcdma CellIdentityWcdma defined in radio/1.0, 1.2, 1.5/types.hal
+     * Convert a CellIdentityWcdma defined in radio/1.2, 1.5/types.hal to CellIdentityWcdma
+     * @param wcdma CellIdentityWcdma defined in radio/1.2, 1.5/types.hal
      * @return The converted CellIdentityWcdma
      */
     public static CellIdentityWcdma convertHalCellIdentityWcdma(Object wcdma) {
         if (wcdma == null) return null;
-        if (wcdma instanceof android.hardware.radio.V1_0.CellIdentityWcdma) {
-            android.hardware.radio.V1_0.CellIdentityWcdma ci =
-                    (android.hardware.radio.V1_0.CellIdentityWcdma) wcdma;
-            return new CellIdentityWcdma(ci.lac, ci.cid, ci.psc, ci.uarfcn, ci.mcc, ci.mnc, "", "",
-                    new ArraySet<>(), null);
-        } else if (wcdma instanceof android.hardware.radio.V1_2.CellIdentityWcdma) {
+        if (wcdma instanceof android.hardware.radio.V1_2.CellIdentityWcdma) {
             android.hardware.radio.V1_2.CellIdentityWcdma ci =
                     (android.hardware.radio.V1_2.CellIdentityWcdma) wcdma;
             return new CellIdentityWcdma(ci.base.lac, ci.base.cid, ci.base.psc, ci.base.uarfcn,
@@ -2928,18 +2768,13 @@
     }
 
     /**
-     * Convert a CellIdentityTdscdma defined in radio/1.0, 1.2, 1.5/types.hal to CellIdentityTdscdma
-     * @param tdscdma CellIdentityTdscdma defined in radio/1.0, 1.2, 1.5/types.hal
+     * Convert a CellIdentityTdscdma defined in radio/1.2, 1.5/types.hal to CellIdentityTdscdma
+     * @param tdscdma CellIdentityTdscdma defined in radio/1.2, 1.5/types.hal
      * @return The converted CellIdentityTdscdma
      */
     public static CellIdentityTdscdma convertHalCellIdentityTdscdma(Object tdscdma) {
         if (tdscdma == null) return null;
-        if (tdscdma instanceof android.hardware.radio.V1_0.CellIdentityTdscdma) {
-            android.hardware.radio.V1_0.CellIdentityTdscdma ci =
-                    (android.hardware.radio.V1_0.CellIdentityTdscdma) tdscdma;
-            return new CellIdentityTdscdma(ci.mcc, ci.mnc, ci.lac, ci.cid, ci.cpid,
-                    CellInfo.UNAVAILABLE, "", "", Collections.emptyList(), null);
-        } else if (tdscdma instanceof android.hardware.radio.V1_2.CellIdentityTdscdma) {
+        if (tdscdma instanceof android.hardware.radio.V1_2.CellIdentityTdscdma) {
             android.hardware.radio.V1_2.CellIdentityTdscdma ci =
                     (android.hardware.radio.V1_2.CellIdentityTdscdma) tdscdma;
             return new CellIdentityTdscdma(ci.base.mcc, ci.base.mnc, ci.base.lac, ci.base.cid,
@@ -2959,7 +2794,7 @@
 
     /**
      * Convert a CellIdentityTdscdma defined in CellIdentityTdscdma.aidl to CellIdentityTdscdma
-     * @param cid CellIdentityTdscdma defined in radio/1.0, 1.2, 1.5/types.hal
+     * @param cid CellIdentityTdscdma defined in radio/1.2, 1.5/types.hal
      * @return The converted CellIdentityTdscdma
      */
     public static CellIdentityTdscdma convertHalCellIdentityTdscdma(
@@ -3008,31 +2843,13 @@
     }
 
     /**
-     * Convert a SignalStrength defined in radio/1.0, 1.2, 1.4, 1.6/types.hal to SignalStrength
-     * @param ss SignalStrength defined in radio/1.0, 1.2, 1.4, 1.6/types.hal
+     * Convert a SignalStrength defined in radio/1.4, 1.6/types.hal to SignalStrength
+     * @param ss SignalStrength defined in radio/1.4, 1.6/types.hal
      * @return The converted SignalStrength
      */
     public static SignalStrength convertHalSignalStrength(Object ss) {
         if (ss == null) return null;
-        if (ss instanceof android.hardware.radio.V1_0.SignalStrength) {
-            android.hardware.radio.V1_0.SignalStrength signalStrength =
-                    (android.hardware.radio.V1_0.SignalStrength) ss;
-            return new SignalStrength(
-                    convertHalCdmaSignalStrength(signalStrength.cdma, signalStrength.evdo),
-                    convertHalGsmSignalStrength(signalStrength.gw), new CellSignalStrengthWcdma(),
-                    convertHalTdscdmaSignalStrength(signalStrength.tdScdma),
-                    convertHalLteSignalStrength(signalStrength.lte),
-                    new CellSignalStrengthNr());
-        } else if (ss instanceof android.hardware.radio.V1_2.SignalStrength) {
-            android.hardware.radio.V1_2.SignalStrength signalStrength =
-                    (android.hardware.radio.V1_2.SignalStrength) ss;
-            return new SignalStrength(
-                    convertHalCdmaSignalStrength(signalStrength.cdma, signalStrength.evdo),
-                    convertHalGsmSignalStrength(signalStrength.gsm),
-                    convertHalWcdmaSignalStrength(signalStrength.wcdma),
-                    convertHalTdscdmaSignalStrength(signalStrength.tdScdma),
-                    convertHalLteSignalStrength(signalStrength.lte), new CellSignalStrengthNr());
-        } else if (ss instanceof android.hardware.radio.V1_4.SignalStrength) {
+        if (ss instanceof android.hardware.radio.V1_4.SignalStrength) {
             android.hardware.radio.V1_4.SignalStrength signalStrength =
                     (android.hardware.radio.V1_4.SignalStrength) ss;
             return new SignalStrength(
@@ -3180,29 +2997,19 @@
     }
 
     /**
-     * Convert a WcdmaSignalStrength defined in radio/1.0, 1.2/types.hal to CellSignalStrengthWcdma
-     * @param wcdma WcdmaSignalStrength defined in radio/1.0, 1.2/types.hal
+     * Convert a WcdmaSignalStrength defined in radio/1.2/types.hal to CellSignalStrengthWcdma
+     * @param wcdma WcdmaSignalStrength defined in radio/1.2/types.hal
      * @return The converted CellSignalStrengthWcdma
      */
     public static CellSignalStrengthWcdma convertHalWcdmaSignalStrength(Object wcdma) {
         if (wcdma == null) return null;
-        CellSignalStrengthWcdma ret = null;
-        if (wcdma instanceof android.hardware.radio.V1_0.WcdmaSignalStrength) {
-            android.hardware.radio.V1_0.WcdmaSignalStrength ss =
-                    (android.hardware.radio.V1_0.WcdmaSignalStrength) wcdma;
-            ret = new CellSignalStrengthWcdma(
-                    CellSignalStrength.getRssiDbmFromAsu(ss.signalStrength), ss.bitErrorRate,
-                    CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE);
-        } else if (wcdma instanceof android.hardware.radio.V1_2.WcdmaSignalStrength) {
-            android.hardware.radio.V1_2.WcdmaSignalStrength ss =
-                    (android.hardware.radio.V1_2.WcdmaSignalStrength) wcdma;
-            ret = new CellSignalStrengthWcdma(
-                    CellSignalStrength.getRssiDbmFromAsu(ss.base.signalStrength),
-                    ss.base.bitErrorRate, CellSignalStrength.getRscpDbmFromAsu(ss.rscp),
-                    CellSignalStrength.getEcNoDbFromAsu(ss.ecno));
-        }
-        if (ret != null && ret.getRssi() == CellInfo.UNAVAILABLE
-                && ret.getRscp() == CellInfo.UNAVAILABLE) {
+        android.hardware.radio.V1_2.WcdmaSignalStrength ss =
+                (android.hardware.radio.V1_2.WcdmaSignalStrength) wcdma;
+        CellSignalStrengthWcdma ret = new CellSignalStrengthWcdma(
+                CellSignalStrength.getRssiDbmFromAsu(ss.base.signalStrength),
+                ss.base.bitErrorRate, CellSignalStrength.getRscpDbmFromAsu(ss.rscp),
+                CellSignalStrength.getEcNoDbFromAsu(ss.ecno));
+        if (ret.getRssi() == CellInfo.UNAVAILABLE && ret.getRscp() == CellInfo.UNAVAILABLE) {
             ret.setDefaultValues();
             ret.updateLevel(null, null);
         }
@@ -3228,29 +3035,18 @@
     }
 
     /**
-     * Convert a TdScdmaSignalStrength defined in radio/1.0/types.hal or TdscdmaSignalStrength
-     * defined in radio/1.2/types.hal to CellSignalStrengthTdscdma
-     * @param tdscdma TdScdmaSignalStrength defined in radio/1.0/types.hal or TdscdmaSignalStrength
-     *        defined in radio/1.2/types.hal
+     * Convert a TdscdmaSignalStrength defined in radio/1.2/types.hal to CellSignalStrengthTdscdma
+     * @param tdscdma TdscdmaSignalStrength defined in radio/1.2/types.hal
      * @return The converted CellSignalStrengthTdscdma
      */
     public static CellSignalStrengthTdscdma convertHalTdscdmaSignalStrength(Object tdscdma) {
         if (tdscdma == null) return null;
-        CellSignalStrengthTdscdma ret = null;
-        if (tdscdma instanceof android.hardware.radio.V1_0.TdScdmaSignalStrength) {
-            android.hardware.radio.V1_0.TdScdmaSignalStrength ss =
-                    (android.hardware.radio.V1_0.TdScdmaSignalStrength) tdscdma;
-            ret = new CellSignalStrengthTdscdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
-                    ss.rscp != CellInfo.UNAVAILABLE ? -ss.rscp : ss.rscp);
-        } else if (tdscdma instanceof android.hardware.radio.V1_2.TdscdmaSignalStrength) {
-            android.hardware.radio.V1_2.TdscdmaSignalStrength ss =
-                    (android.hardware.radio.V1_2.TdscdmaSignalStrength) tdscdma;
-            ret = new CellSignalStrengthTdscdma(
-                    CellSignalStrength.getRssiDbmFromAsu(ss.signalStrength), ss.bitErrorRate,
-                    CellSignalStrength.getRscpDbmFromAsu(ss.rscp));
-        }
-        if (ret != null && ret.getRssi() == CellInfo.UNAVAILABLE
-                && ret.getRscp() == CellInfo.UNAVAILABLE) {
+        android.hardware.radio.V1_2.TdscdmaSignalStrength ss =
+                (android.hardware.radio.V1_2.TdscdmaSignalStrength) tdscdma;
+        CellSignalStrengthTdscdma ret = new CellSignalStrengthTdscdma(
+                CellSignalStrength.getRssiDbmFromAsu(ss.signalStrength), ss.bitErrorRate,
+                CellSignalStrength.getRscpDbmFromAsu(ss.rscp));
+        if (ret.getRssi() == CellInfo.UNAVAILABLE && ret.getRscp() == CellInfo.UNAVAILABLE) {
             ret.setDefaultValues();
             ret.updateLevel(null, null);
         }
@@ -3435,9 +3231,9 @@
     }
 
     /**
-     * Convert SetupDataCallResult defined in radio/1.0, 1.4, 1.5, 1.6/types.hal into
+     * Convert SetupDataCallResult defined in radio/1.4, 1.5, 1.6/types.hal into
      * DataCallResponse
-     * @param dcResult SetupDataCallResult defined in radio/1.0, 1.4, 1.5, 1.6/types.hal
+     * @param dcResult SetupDataCallResult defined in radio/1.4, 1.5, 1.6/types.hal
      * @return The converted DataCallResponse
      */
     @VisibleForTesting
@@ -3448,10 +3244,10 @@
         long suggestedRetryTime;
         String ifname;
         int protocolType;
-        String[] addresses = null;
-        String[] dnses = null;
-        String[] gateways = null;
-        String[] pcscfs = null;
+        String[] addresses;
+        String[] dnses;
+        String[] gateways;
+        String[] pcscfs;
         Qos defaultQos = null;
         @DataCallResponse.HandoverFailureMode
         int handoverFailureMode = DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY;
@@ -3461,34 +3257,7 @@
         NetworkSliceInfo sliceInfo = null;
         List<TrafficDescriptor> trafficDescriptors = new ArrayList<>();
 
-        if (dcResult instanceof android.hardware.radio.V1_0.SetupDataCallResult) {
-            final android.hardware.radio.V1_0.SetupDataCallResult result =
-                    (android.hardware.radio.V1_0.SetupDataCallResult) dcResult;
-            cause = result.status;
-            suggestedRetryTime = result.suggestedRetryTime;
-            cid = result.cid;
-            active = result.active;
-            protocolType = ApnSetting.getProtocolIntFromString(result.type);
-            ifname = result.ifname;
-            if (!TextUtils.isEmpty(result.addresses)) {
-                addresses = result.addresses.split("\\s+");
-            }
-            if (!TextUtils.isEmpty(result.dnses)) {
-                dnses = result.dnses.split("\\s+");
-            }
-            if (!TextUtils.isEmpty(result.gateways)) {
-                gateways = result.gateways.split("\\s+");
-            }
-            if (!TextUtils.isEmpty(result.pcscf)) {
-                pcscfs = result.pcscf.split("\\s+");
-            }
-            mtu = mtuV4 = mtuV6 = result.mtu;
-            if (addresses != null) {
-                for (String address : addresses) {
-                    laList.add(convertToLinkAddress(address));
-                }
-            }
-        } else if (dcResult instanceof android.hardware.radio.V1_4.SetupDataCallResult) {
+        if (dcResult instanceof android.hardware.radio.V1_4.SetupDataCallResult) {
             final android.hardware.radio.V1_4.SetupDataCallResult result =
                     (android.hardware.radio.V1_4.SetupDataCallResult) dcResult;
             cause = result.cause;
@@ -4040,10 +3809,10 @@
     }
 
     /**
-     * Convert a list of SetupDataCallResult defined in radio/1.0, 1.4, 1.5, 1.6/types.hal into
+     * Convert a list of SetupDataCallResult defined in radio/1.4, 1.5, 1.6/types.hal into
      * a list of DataCallResponse
      * @param dataCallResultList List of SetupDataCallResult defined in
-     *        radio/1.0, 1.4, 1.5, 1.6/types.hal
+     *        radio/1.4, 1.5, 1.6/types.hal
      * @return The converted list of DataCallResponses
      */
     @VisibleForTesting
@@ -4133,8 +3902,8 @@
     }
 
     /**
-     * Convert Call defined in radio/1.0, 1.2, 1.6/types.hal to DriverCall
-     * @param halCall Call defined in radio/1.0, 1.2, 1.6/types.hal
+     * Convert Call defined in radio/1.2, 1.6/types.hal to DriverCall
+     * @param halCall Call defined in radio/1.2, 1.6/types.hal
      * @return The converted DriverCall
      */
     public static DriverCall convertToDriverCall(Object halCall) {
@@ -4150,17 +3919,13 @@
             call16 = null;
             call12 = (android.hardware.radio.V1_2.Call) halCall;
             call10 = call12.base;
-        } else if (halCall instanceof android.hardware.radio.V1_0.Call) {
-            call16 = null;
-            call12 = null;
-            call10 = (android.hardware.radio.V1_0.Call) halCall;
         } else {
             call16 = null;
             call12 = null;
             call10 = null;
         }
         if (call10 != null) {
-            dc.state = DriverCall.stateFromCLCC((int) (call10.state));
+            dc.state = DriverCall.stateFromCLCC(call10.state);
             dc.index = call10.index;
             dc.TOA = call10.toa;
             dc.isMpty = call10.isMpty;
@@ -4169,10 +3934,9 @@
             dc.isVoice = call10.isVoice;
             dc.isVoicePrivacy = call10.isVoicePrivacy;
             dc.number = call10.number;
-            dc.numberPresentation = DriverCall.presentationFromCLIP(
-                    (int) (call10.numberPresentation));
+            dc.numberPresentation = DriverCall.presentationFromCLIP(call10.numberPresentation);
             dc.name = call10.name;
-            dc.namePresentation = DriverCall.presentationFromCLIP((int) (call10.namePresentation));
+            dc.namePresentation = DriverCall.presentationFromCLIP(call10.namePresentation);
             if (call10.uusInfo.size() == 1) {
                 dc.uusInfo = new UUSInfo();
                 dc.uusInfo.setType(call10.uusInfo.get(0).uusType);
@@ -4186,7 +3950,7 @@
             dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
         }
         if (call12 != null) {
-            dc.audioQuality = (int) (call12.audioQuality);
+            dc.audioQuality = call12.audioQuality;
         }
         if (call16 != null) {
             dc.forwardedNumber = call16.forwardedNumber;
@@ -4201,7 +3965,7 @@
      */
     public static DriverCall convertToDriverCall(android.hardware.radio.voice.Call halCall) {
         DriverCall dc = new DriverCall();
-        dc.state = DriverCall.stateFromCLCC((int) halCall.state);
+        dc.state = DriverCall.stateFromCLCC(halCall.state);
         dc.index = halCall.index;
         dc.TOA = halCall.toa;
         dc.isMpty = halCall.isMpty;
@@ -4210,9 +3974,9 @@
         dc.isVoice = halCall.isVoice;
         dc.isVoicePrivacy = halCall.isVoicePrivacy;
         dc.number = halCall.number;
-        dc.numberPresentation = DriverCall.presentationFromCLIP((int) halCall.numberPresentation);
+        dc.numberPresentation = DriverCall.presentationFromCLIP(halCall.numberPresentation);
         dc.name = halCall.name;
-        dc.namePresentation = DriverCall.presentationFromCLIP((int) halCall.namePresentation);
+        dc.namePresentation = DriverCall.presentationFromCLIP(halCall.namePresentation);
         if (halCall.uusInfo.length == 1) {
             dc.uusInfo = new UUSInfo();
             dc.uusInfo.setType(halCall.uusInfo[0].uusType);
@@ -4223,7 +3987,7 @@
         }
         // Make sure there's a leading + on addresses with a TOA of 145
         dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
-        dc.audioQuality = (int) halCall.audioQuality;
+        dc.audioQuality = halCall.audioQuality;
         dc.forwardedNumber = halCall.forwardedNumber;
         return dc;
     }
@@ -5377,6 +5141,38 @@
                 return "SET_N1_MODE_ENABLED";
             case RIL_REQUEST_IS_N1_MODE_ENABLED:
                 return "IS_N1_MODE_ENABLED";
+            case RIL_REQUEST_SET_LOCATION_PRIVACY_SETTING:
+                return "SET_LOCATION_PRIVACY_SETTING";
+            case RIL_REQUEST_GET_LOCATION_PRIVACY_SETTING:
+                return "GET_LOCATION_PRIVACY_SETTING";
+            case RIL_REQUEST_GET_SATELLITE_CAPABILITIES:
+                return "GET_SATELLITE_CAPABILITIES";
+            case RIL_REQUEST_SET_SATELLITE_POWER:
+                return "SET_SATELLITE_POWER";
+            case RIL_REQUEST_GET_SATELLITE_POWER:
+                return "GET_SATELLITE_POWER";
+            case RIL_REQUEST_PROVISION_SATELLITE_SERVICE:
+                return "PROVISION_SATELLITE_SERVICE";
+            case RIL_REQUEST_ADD_ALLOWED_SATELLITE_CONTACTS:
+                return "ADD_ALLOWED_SATELLITE_CONTACTS";
+            case RIL_REQUEST_REMOVE_ALLOWED_SATELLITE_CONTACTS:
+                return "REMOVE_ALLOWED_SATELLITE_CONTACTS";
+            case RIL_REQUEST_SEND_SATELLITE_MESSAGES:
+                return "SEND_SATELLITE_MESSAGES";
+            case RIL_REQUEST_GET_PENDING_SATELLITE_MESSAGES:
+                return "GET_PENDING_SATELLITE_MESSAGES";
+            case RIL_REQUEST_GET_SATELLITE_MODE:
+                return "GET_SATELLITE_MODE";
+            case RIL_REQUEST_SET_SATELLITE_INDICATION_FILTER:
+                return "SET_SATELLITE_INDICATION_FILTER";
+            case RIL_REQUEST_START_SENDING_SATELLITE_POINTING_INFO:
+                return "START_SENDING_SATELLITE_POINTING_INFO";
+            case RIL_REQUEST_STOP_SENDING_SATELLITE_POINTING_INFO:
+                return "STOP_SENDING_SATELLITE_POINTING_INFO";
+            case RIL_REQUEST_GET_MAX_CHARACTERS_PER_SATELLITE_TEXT_MESSAGE:
+                return "GET_MAX_CHARACTERS_PER_SATELLITE_TEXT_MESSAGE";
+            case RIL_REQUEST_GET_TIME_FOR_NEXT_SATELLITE_VISIBILITY:
+                return "GET_TIME_FOR_NEXT_SATELLITE_VISIBILITY";
             default:
                 return "<unknown request " + request + ">";
         }
@@ -5499,6 +5295,20 @@
                 return "UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED";
             case RIL_UNSOL_SLICING_CONFIG_CHANGED:
                 return "UNSOL_SLICING_CONFIG_CHANGED";
+            case RIL_UNSOL_PENDING_SATELLITE_MESSAGE_COUNT:
+                return "UNSOL_PENDING_SATELLITE_MESSAGE_COUNT";
+            case RIL_UNSOL_NEW_SATELLITE_MESSAGES:
+                return "UNSOL_NEW_SATELLITE_MESSAGES";
+            case RIL_UNSOL_SATELLITE_MESSAGES_TRANSFER_COMPLETE:
+                return "UNSOL_SATELLITE_MESSAGES_TRANSFER_COMPLETE";
+            case RIL_UNSOL_SATELLITE_POINTING_INFO_CHANGED:
+                return "UNSOL_SATELLITE_POINTING_INFO_CHANGED";
+            case RIL_UNSOL_SATELLITE_MODE_CHANGED:
+                return "UNSOL_SATELLITE_MODE_CHANGED";
+            case RIL_UNSOL_SATELLITE_RADIO_TECHNOLOGY_CHANGED:
+                return "UNSOL_SATELLITE_RADIO_TECHNOLOGY_CHANGED";
+            case RIL_UNSOL_SATELLITE_PROVISION_STATE_CHANGED:
+                return "UNSOL_SATELLITE_PROVISION_STATE_CHANGED";
             /* The follow unsols are not defined in RIL.h */
             case RIL_UNSOL_ICC_SLOT_STATUS:
                 return "UNSOL_ICC_SLOT_STATUS";
@@ -5905,12 +5715,52 @@
     }
 
     /**
+     * Convert android.hardware.radio.satellite.SatelliteCapabilities to
+     * android.telephony.satellite.SatelliteCapabilities
+     */
+    public static SatelliteCapabilities convertHalSatelliteCapabilities(
+            android.hardware.radio.satellite.SatelliteCapabilities capabilities) {
+        Set<Integer> supportedRadioTechnologies = new HashSet<>();
+        if (capabilities.supportedRadioTechnologies != null
+                && capabilities.supportedRadioTechnologies.length > 0) {
+            for (int technology : capabilities.supportedRadioTechnologies) {
+                supportedRadioTechnologies.add(technology);
+            }
+        }
+        return new SatelliteCapabilities(supportedRadioTechnologies,
+                capabilities.needsPointingToSatellite, 0, null);
+    }
+
+    /**
+     * Convert from android.hardware.radio.satellite.PointingInfo to
+     * android.telephony.satellite.stub.PointingInfo
+     */
+    public static PointingInfo convertHalSatellitePointingInfo(
+            android.hardware.radio.satellite.PointingInfo pointingInfo) {
+        return new PointingInfo(pointingInfo.satelliteAzimuthDegrees,
+                pointingInfo.satelliteElevationDegrees);
+    }
+
+    /**
+     * Convert from android.telephony.satellite.stub.PointingInfo to
+     * android.hardware.radio.satellite.PointingInfo
+     */
+    public static android.hardware.radio.satellite.PointingInfo convertToHalSatellitePointingInfo(
+            PointingInfo pointingInfo) {
+        android.hardware.radio.satellite.PointingInfo halPointingInfo =
+                new android.hardware.radio.satellite.PointingInfo();
+        halPointingInfo.satelliteAzimuthDegrees = pointingInfo.getSatelliteAzimuthDegrees();
+        halPointingInfo.satelliteElevationDegrees = pointingInfo.getSatelliteElevationDegrees();
+        return halPointingInfo;
+    }
+
+    /**
      * Convert satellite-related errors from CommandException.Error to
      * SatelliteManager.SatelliteServiceResult.
      * @param error The satellite error.
      * @return The converted SatelliteServiceResult.
      */
-    @SatelliteManager.SatelliteError
+    @SatelliteManager.SatelliteResult
     public static int convertToSatelliteError(
             CommandException.Error error) {
         switch (error) {
@@ -5919,29 +5769,35 @@
             case MODEM_ERR:
                 //fallthrough to SYSTEM_ERR
             case SYSTEM_ERR:
-                return SatelliteManager.SATELLITE_MODEM_ERROR;
+                return SatelliteManager.SATELLITE_RESULT_MODEM_ERROR;
             case INVALID_ARGUMENTS:
-                return SatelliteManager.SATELLITE_INVALID_ARGUMENTS;
+                return SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS;
             case INVALID_MODEM_STATE:
-                return SatelliteManager.SATELLITE_INVALID_MODEM_STATE;
+                return SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE;
             case RADIO_NOT_AVAILABLE:
-                return SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE;
+                return SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE;
             case REQUEST_NOT_SUPPORTED:
-                return SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED;
+                return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
             case NO_MEMORY:
                 //fallthrough to NO_RESOURCES
             case NO_RESOURCES:
-                return SatelliteManager.SATELLITE_NO_RESOURCES;
+                return SatelliteManager.SATELLITE_RESULT_NO_RESOURCES;
             case NETWORK_ERR:
-                return SatelliteManager.SATELLITE_NETWORK_ERROR;
+                return SatelliteManager.SATELLITE_RESULT_NETWORK_ERROR;
+            case NETWORK_TIMEOUT:
+                return SatelliteManager.SATELLITE_RESULT_NETWORK_TIMEOUT;
             case NO_NETWORK_FOUND:
-                return SatelliteManager.SATELLITE_NOT_REACHABLE;
+                //fallthrough to NO_SATELLITE_SIGNAL
+            case NO_SATELLITE_SIGNAL:
+                return SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE;
             case ABORTED:
-                return SatelliteManager.SATELLITE_REQUEST_ABORTED;
+                return SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED;
             case ACCESS_BARRED:
-                return SatelliteManager.SATELLITE_ACCESS_BARRED;
+                return SatelliteManager.SATELLITE_RESULT_ACCESS_BARRED;
+            case SUBSCRIBER_NOT_AUTHORIZED:
+                return SatelliteManager.SATELLITE_RESULT_NOT_AUTHORIZED;
             default:
-                return SatelliteManager.SATELLITE_ERROR;
+                return SatelliteManager.SATELLITE_RESULT_ERROR;
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
index 3e2be1d..6bf0203 100644
--- a/src/java/com/android/internal/telephony/RadioConfig.java
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -61,7 +61,6 @@
     static final int EVENT_HIDL_SERVICE_DEAD = 1;
     static final int EVENT_AIDL_SERVICE_DEAD = 2;
     static final HalVersion RADIO_CONFIG_HAL_VERSION_UNKNOWN = new HalVersion(-1, -1);
-    static final HalVersion RADIO_CONFIG_HAL_VERSION_1_0 = new HalVersion(1, 0);
     static final HalVersion RADIO_CONFIG_HAL_VERSION_1_1 = new HalVersion(1, 1);
     static final HalVersion RADIO_CONFIG_HAL_VERSION_1_3 = new HalVersion(1, 3);
     static final HalVersion RADIO_CONFIG_HAL_VERSION_2_0 = new HalVersion(2, 0);
@@ -317,13 +316,7 @@
         }
 
         if (mRadioConfigProxy.isEmpty()) {
-            try {
-                mRadioConfigProxy.setHidl(RADIO_CONFIG_HAL_VERSION_1_0,
-                        android.hardware.radio.config.V1_0.IRadioConfig.getService(true));
-            } catch (RemoteException | NoSuchElementException e) {
-                mRadioConfigProxy.clear();
-                loge("getHidlRadioConfigProxy1_0: RadioConfigProxy getService | linkToDeath: " + e);
-            }
+            loge("IRadioConfig <1.1 is no longer supported.");
         }
 
         if (!mRadioConfigProxy.isEmpty()) {
diff --git a/src/java/com/android/internal/telephony/RadioConfigProxy.java b/src/java/com/android/internal/telephony/RadioConfigProxy.java
index edeb558..9d05fc5 100644
--- a/src/java/com/android/internal/telephony/RadioConfigProxy.java
+++ b/src/java/com/android/internal/telephony/RadioConfigProxy.java
@@ -35,7 +35,7 @@
     private final RadioConfigHidlServiceDeathRecipient mRadioConfigHidlServiceDeathRecipient;
     private final RadioConfigAidlServiceDeathRecipient mRadioConfigAidlServiceDeathRecipient;
 
-    private volatile android.hardware.radio.config.V1_0.IRadioConfig mHidlRadioConfigProxy = null;
+    private volatile android.hardware.radio.config.V1_1.IRadioConfig mHidlRadioConfigProxy = null;
     private volatile android.hardware.radio.config.IRadioConfig mAidlRadioConfigProxy = null;
 
     private HalVersion mRadioConfigHalVersion = RadioConfig.RADIO_CONFIG_HAL_VERSION_UNKNOWN;
@@ -57,7 +57,7 @@
      */
     public void setHidl(
             HalVersion radioConfigHalVersion,
-            android.hardware.radio.config.V1_0.IRadioConfig radioConfig) {
+            android.hardware.radio.config.V1_1.IRadioConfig radioConfig) {
         mRadioConfigHalVersion = radioConfigHalVersion;
         mHidlRadioConfigProxy = radioConfig;
         mIsAidl = false;
@@ -65,19 +65,11 @@
     }
 
     /**
-     * Get HIDL IRadioConfig V1_0
-     * @return IRadioConfigV1_0
-     */
-    public android.hardware.radio.config.V1_0.IRadioConfig getHidl10() {
-        return mHidlRadioConfigProxy;
-    }
-
-    /**
      * Get HIDL IRadioConfig V1_1
      * @return IRadioConfigV1_1
      */
     public android.hardware.radio.config.V1_1.IRadioConfig getHidl11() {
-        return (android.hardware.radio.config.V1_1.IRadioConfig) mHidlRadioConfigProxy;
+        return mHidlRadioConfigProxy;
     }
 
     /**
@@ -192,7 +184,7 @@
         if (isAidl()) {
             getAidl().getSimSlotsStatus(serial);
         } else {
-            getHidl10().getSimSlotsStatus(serial);
+            getHidl11().getSimSlotsStatus(serial);
         }
     }
 
@@ -227,7 +219,7 @@
         if (isAidl()) {
             getAidl().setSimSlotsMapping(serial, RILUtils.convertSimSlotsMapping(slotMapping));
         } else {
-            getHidl10().setSimSlotsMapping(serial,
+            getHidl11().setSimSlotsMapping(serial,
                     RILUtils.convertSlotMappingToList(slotMapping));
         }
     }
@@ -265,13 +257,13 @@
         private static final String TAG = "RadioConfigHidlSDR";
 
         private final RadioConfig mRadioConfig;
-        private android.hardware.radio.config.V1_0.IRadioConfig mService;
+        private android.hardware.radio.config.V1_1.IRadioConfig mService;
 
         RadioConfigHidlServiceDeathRecipient(RadioConfig radioConfig) {
             mRadioConfig = radioConfig;
         }
 
-        public void setService(android.hardware.radio.config.V1_0.IRadioConfig service) {
+        public void setService(android.hardware.radio.config.V1_1.IRadioConfig service) {
             mService = service;
         }
 
diff --git a/src/java/com/android/internal/telephony/RadioDataProxy.java b/src/java/com/android/internal/telephony/RadioDataProxy.java
index 9671077..40db9e5 100644
--- a/src/java/com/android/internal/telephony/RadioDataProxy.java
+++ b/src/java/com/android/internal/telephony/RadioDataProxy.java
@@ -22,9 +22,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.Rlog;
-import android.telephony.ServiceState;
 import android.telephony.data.DataProfile;
-import android.telephony.data.DataService;
 import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.TrafficDescriptor;
 
@@ -34,8 +32,8 @@
 import java.util.ArrayList;
 
 /**
- * A holder for IRadioData. Use getHidl to get IRadio 1.0 and call the HIDL implementations or
- * getAidl to get IRadioData and call the AIDL implementations of the HAL APIs.
+ * A holder for IRadioData.
+ * Use getAidl to get IRadioData and call the AIDL implementations of the HAL APIs.
  */
 public class RadioDataProxy extends RadioServiceProxy {
     private static final String TAG = "RadioDataProxy";
@@ -129,12 +127,8 @@
         if (isEmpty()) return;
         if (isAidl()) {
             mDataProxy.deactivateDataCall(serial, cid, reason);
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_2)) {
-            ((android.hardware.radio.V1_2.IRadio) mRadioProxy).deactivateDataCall_1_2(
-                    serial, cid, reason);
         } else {
-            mRadioProxy.deactivateDataCall(serial, cid,
-                    reason == DataService.REQUEST_REASON_SHUTDOWN);
+            mRadioProxy.deactivateDataCall_1_2(serial, cid, reason);
         }
     }
 
@@ -216,11 +210,9 @@
      * Call IRadioData#setDataProfile
      * @param serial Serial number of request
      * @param profiles Array of DataProfiles to set
-     * @param isRoaming Whether or not the device is roaming
      * @throws RemoteException
      */
-    public void setDataProfile(int serial, DataProfile[] profiles, boolean isRoaming)
-            throws RemoteException {
+    public void setDataProfile(int serial, DataProfile[] profiles) throws RemoteException {
         if (isEmpty()) return;
         if (isAidl()) {
             android.hardware.radio.data.DataProfileInfo[] dpis =
@@ -235,22 +227,12 @@
                 dpis.add(RILUtils.convertToHalDataProfile15(dp));
             }
             ((android.hardware.radio.V1_5.IRadio) mRadioProxy).setDataProfile_1_5(serial, dpis);
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
+        } else {
             ArrayList<android.hardware.radio.V1_4.DataProfileInfo> dpis = new ArrayList<>();
             for (DataProfile dp : profiles) {
                 dpis.add(RILUtils.convertToHalDataProfile14(dp));
             }
-            ((android.hardware.radio.V1_4.IRadio) mRadioProxy).setDataProfile_1_4(serial, dpis);
-        } else {
-            ArrayList<android.hardware.radio.V1_0.DataProfileInfo> dpis = new ArrayList<>();
-            for (DataProfile dp : profiles) {
-                if (dp.isPersistent()) {
-                    dpis.add(RILUtils.convertToHalDataProfile10(dp));
-                }
-            }
-            if (!dpis.isEmpty()) {
-                mRadioProxy.setDataProfile(serial, dpis, isRoaming);
-            }
+            mRadioProxy.setDataProfile_1_4(serial, dpis);
         }
     }
 
@@ -277,10 +259,9 @@
      * Call IRadioData#setInitialAttachApn
      * @param serial Serial number of request
      * @param dataProfile Data profile containing APN settings
-     * @param isRoaming Whether or not the device is roaming
      * @throws RemoteException
      */
-    public void setInitialAttachApn(int serial, DataProfile dataProfile, boolean isRoaming)
+    public void setInitialAttachApn(int serial, DataProfile dataProfile)
             throws RemoteException {
         if (isEmpty()) return;
         if (isAidl()) {
@@ -288,22 +269,17 @@
         } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
             ((android.hardware.radio.V1_5.IRadio) mRadioProxy).setInitialAttachApn_1_5(serial,
                     RILUtils.convertToHalDataProfile15(dataProfile));
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
-            ((android.hardware.radio.V1_4.IRadio) mRadioProxy).setInitialAttachApn_1_4(serial,
-                    RILUtils.convertToHalDataProfile14(dataProfile));
         } else {
-            mRadioProxy.setInitialAttachApn(serial, RILUtils.convertToHalDataProfile10(dataProfile),
-                    dataProfile.isPersistent(), isRoaming);
+            mRadioProxy.setInitialAttachApn_1_4(serial,
+                    RILUtils.convertToHalDataProfile14(dataProfile));
         }
     }
 
     /**
      * Call IRadioData#setupDataCall
      * @param serial Serial number of request
-     * @param phoneId Phone ID of the requestor
      * @param accessNetwork Access network to setup the data call
      * @param dataProfileInfo Data profile info
-     * @param isRoaming Whether or not the device is roaming
      * @param roamingAllowed Whether or not data roaming is allowed by the user
      * @param reason Request reason
      * @param linkProperties LinkProperties containing address and DNS info
@@ -316,15 +292,14 @@
      *                            is allowed
      * @throws RemoteException
      */
-    public void setupDataCall(int serial, int phoneId, int accessNetwork,
-            DataProfile dataProfileInfo, boolean isRoaming, boolean roamingAllowed, int reason,
-            LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo,
-            TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed)
-            throws RemoteException {
+    public void setupDataCall(int serial, int accessNetwork, DataProfile dataProfileInfo,
+            boolean roamingAllowed, int reason, LinkProperties linkProperties, int pduSessionId,
+            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+            boolean matchAllRuleAllowed) throws RemoteException {
         if (isEmpty()) return;
         ArrayList<String> addresses = new ArrayList<>();
         ArrayList<String> dnses = new ArrayList<>();
-        String[] dnsesArr = null;
+        String[] dnsesArr;
         if (linkProperties != null) {
             for (InetAddress address : linkProperties.getAddresses()) {
                 addresses.add(address.getHostAddress());
@@ -361,31 +336,10 @@
                     accessNetwork, RILUtils.convertToHalDataProfile15(dataProfileInfo),
                     roamingAllowed, reason, RILUtils.convertToHalLinkProperties15(linkProperties),
                     dnses);
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
-            ((android.hardware.radio.V1_4.IRadio) mRadioProxy).setupDataCall_1_4(serial,
-                    accessNetwork, RILUtils.convertToHalDataProfile14(dataProfileInfo),
-                    roamingAllowed, reason, addresses, dnses);
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_2)) {
-            ((android.hardware.radio.V1_2.IRadio) mRadioProxy).setupDataCall_1_2(serial,
-                    accessNetwork, RILUtils.convertToHalDataProfile10(dataProfileInfo),
-                    dataProfileInfo.isPersistent(), roamingAllowed, isRoaming, reason, addresses,
-                    dnses);
         } else {
-            // Getting data RAT here is just a workaround to support the older 1.0 vendor RIL.
-            // The new data service interface passes access network type instead of RAT for
-            // setup data request. It is impossible to convert access network type back to RAT here,
-            // so we directly get the data RAT from phone.
-            int dataRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
-            Phone phone = PhoneFactory.getPhone(phoneId);
-            if (phone != null) {
-                ServiceState ss = phone.getServiceState();
-                if (ss != null) {
-                    dataRat = ss.getRilDataRadioTechnology();
-                }
-            }
-            mRadioProxy.setupDataCall(serial, dataRat,
-                    RILUtils.convertToHalDataProfile10(dataProfileInfo),
-                    dataProfileInfo.isPersistent(), roamingAllowed, isRoaming);
+            mRadioProxy.setupDataCall_1_4(serial, accessNetwork,
+                    RILUtils.convertToHalDataProfile14(dataProfileInfo),
+                    roamingAllowed, reason, addresses, dnses);
         }
     }
 
@@ -415,7 +369,7 @@
      */
     public void startKeepalive(int serial, int contextId, KeepalivePacketData packetData,
             int intervalMillis, Message result) throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_1)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             android.hardware.radio.data.KeepaliveRequest req =
                     new android.hardware.radio.data.KeepaliveRequest();
@@ -476,7 +430,7 @@
             req.destinationPort = packetData.getDstPort();
             req.maxKeepaliveIntervalMillis = intervalMillis;
 
-            ((android.hardware.radio.V1_1.IRadio) mRadioProxy).startKeepalive(serial, req);
+            mRadioProxy.startKeepalive(serial, req);
         }
     }
 
@@ -487,11 +441,11 @@
      * @throws RemoteException
      */
     public void stopKeepalive(int serial, int sessionHandle) throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_1)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             mDataProxy.stopKeepalive(serial, sessionHandle);
         } else {
-            ((android.hardware.radio.V1_1.IRadio) mRadioProxy).stopKeepalive(serial, sessionHandle);
+            mRadioProxy.stopKeepalive(serial, sessionHandle);
         }
     }
 }
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index 4f75412..aadfe62 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -240,28 +240,18 @@
     }
 
     public void currentSignalStrength(int indicationType,
-                                      android.hardware.radio.V1_0.SignalStrength signalStrength) {
-        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
-
-        SignalStrength ssInitial = RILUtils.convertHalSignalStrength(signalStrength);
-
-        SignalStrength ss = mRil.fixupSignalStrength10(ssInitial);
-        // Note this is set to "verbose" because it happens frequently
-        if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
-
-        if (mRil.mSignalStrengthRegistrant != null) {
-            mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult (null, ss, null));
-        }
+            android.hardware.radio.V1_0.SignalStrength signalStrength) {
+        mRil.unsljLogMore(RIL_UNSOL_SIGNAL_STRENGTH, "unsupported on IRadio < 1.4");
     }
 
     /**
      * Indicates current link capacity estimate.
      */
     public void currentLinkCapacityEstimate(int indicationType,
-                                            android.hardware.radio.V1_2.LinkCapacityEstimate lce) {
+            android.hardware.radio.V1_2.LinkCapacityEstimate lce) {
         mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
-        List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
+        List<LinkCapacityEstimate> response = RILUtils.convertHalLinkCapacityEstimate(lce);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
 
@@ -277,7 +267,7 @@
             android.hardware.radio.V1_6.LinkCapacityEstimate lce) {
         mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
-        List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
+        List<LinkCapacityEstimate> response = RILUtils.convertHalLinkCapacityEstimate(lce);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
 
@@ -290,16 +280,8 @@
      * Indicates the current signal strength of the camped or primary serving cell.
      */
     public void currentSignalStrength_1_2(int indicationType,
-                                      android.hardware.radio.V1_2.SignalStrength signalStrength) {
-        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
-
-        SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength);
-        // Note this is set to "verbose" because it happens frequently
-        if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
-
-        if (mRil.mSignalStrengthRegistrant != null) {
-            mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null));
-        }
+            android.hardware.radio.V1_2.SignalStrength signalStrength) {
+        mRil.unsljLogMore(RIL_UNSOL_SIGNAL_STRENGTH, "unsupported on IRadio < 1.4");
     }
 
     /**
@@ -359,8 +341,7 @@
      */
     public void currentPhysicalChannelConfigs(int indicationType,
             ArrayList<android.hardware.radio.V1_2.PhysicalChannelConfig> configs) {
-        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
-        physicalChannelConfigsIndication(configs);
+        mRil.unsljLogMore(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, "unsupported on IRadio < 1.4");
     }
 
     /**
@@ -393,7 +374,7 @@
     /** Indicates current data call list. */
     public void dataCallListChanged(int indicationType,
             ArrayList<android.hardware.radio.V1_0.SetupDataCallResult> dcList) {
-        responseDataCallListChanged(indicationType, dcList);
+        mRil.unsljLogMore(RIL_UNSOL_DATA_CALL_LIST_CHANGED, "unsupported on IRadio < 1.4");
     }
 
     /** Indicates current data call list with radio HAL 1.4. */
@@ -803,15 +784,13 @@
     /** Get unsolicited message for cellInfoList */
     public void cellInfoList(int indicationType,
             ArrayList<android.hardware.radio.V1_0.CellInfo> records) {
-        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
-        responseCellInfoList(records);
+        mRil.unsljLogMore(RIL_UNSOL_CELL_INFO_LIST, "unsupported on IRadio < 1.4");
     }
 
     /** Get unsolicited message for cellInfoList using HAL V1_2 */
     public void cellInfoList_1_2(int indicationType,
             ArrayList<android.hardware.radio.V1_2.CellInfo> records) {
-        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
-        responseCellInfoList(records);
+        mRil.unsljLogMore(RIL_UNSOL_CELL_INFO_LIST, "unsupported on IRadio < 1.4");
     }
 
     /** Get unsolicited message for cellInfoList using HAL V1_4 */
@@ -854,20 +833,20 @@
 
     /** Incremental network scan results */
     public void networkScanResult(int indicationType,
-                                  android.hardware.radio.V1_1.NetworkScanResult result) {
-        responseNetworkScan(indicationType, result);
+            android.hardware.radio.V1_1.NetworkScanResult result) {
+        mRil.unsljLogMore(RIL_UNSOL_NETWORK_SCAN_RESULT, "unsupported on IRadio < 1.4");
     }
 
     /** Incremental network scan results with HAL V1_2 */
     public void networkScanResult_1_2(int indicationType,
-                                      android.hardware.radio.V1_2.NetworkScanResult result) {
-        responseNetworkScan_1_2(indicationType, result);
+            android.hardware.radio.V1_2.NetworkScanResult result) {
+        mRil.unsljLogMore(RIL_UNSOL_NETWORK_SCAN_RESULT, "unsupported on IRadio < 1.4");
     }
 
     /** Incremental network scan results with HAL V1_4 */
     public void networkScanResult_1_4(int indicationType,
-                                      android.hardware.radio.V1_4.NetworkScanResult result) {
-        responseNetworkScan_1_4(indicationType, result);
+            android.hardware.radio.V1_4.NetworkScanResult result) {
+        responseNetworkScan(indicationType, result);
     }
 
     /** Incremental network scan results with HAL V1_5 */
@@ -918,8 +897,7 @@
                 new AsyncResult (null, response, null));
     }
 
-    public void hardwareConfigChanged(
-            int indicationType,
+    public void hardwareConfigChanged(int indicationType,
             ArrayList<android.hardware.radio.V1_0.HardwareConfig> configs) {
         mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
@@ -932,7 +910,7 @@
     }
 
     public void radioCapabilityIndication(int indicationType,
-                                          android.hardware.radio.V1_0.RadioCapability rc) {
+            android.hardware.radio.V1_0.RadioCapability rc) {
         mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         RadioCapability response = RILUtils.convertHalRadioCapability(rc, mRil);
@@ -1002,15 +980,7 @@
     }
 
     public void lceData(int indicationType, LceDataInfo lce) {
-        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
-
-        List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
-
-        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
-
-        if (mRil.mLceInfoRegistrants != null) {
-            mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
-        }
+        mRil.unsljLogMore(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, "unsupported on IRadio < 1.4");
     }
 
     public void pcoData(int indicationType, PcoDataInfo pco) {
@@ -1198,16 +1168,7 @@
         List<PhysicalChannelConfig> response = new ArrayList<>(configs.size());
         try {
             for (Object obj : configs) {
-                if (obj instanceof android.hardware.radio.V1_2.PhysicalChannelConfig) {
-                    android.hardware.radio.V1_2.PhysicalChannelConfig config =
-                            (android.hardware.radio.V1_2.PhysicalChannelConfig) obj;
-
-                    response.add(new PhysicalChannelConfig.Builder()
-                            .setCellConnectionStatus(RILUtils.convertHalCellConnectionStatus(
-                                    config.status))
-                            .setCellBandwidthDownlinkKhz(config.cellBandwidthDownlink)
-                            .build());
-                } else if (obj instanceof android.hardware.radio.V1_4.PhysicalChannelConfig) {
+                if (obj instanceof android.hardware.radio.V1_4.PhysicalChannelConfig) {
                     android.hardware.radio.V1_4.PhysicalChannelConfig config =
                             (android.hardware.radio.V1_4.PhysicalChannelConfig) obj;
                     PhysicalChannelConfig.Builder builder = new PhysicalChannelConfig.Builder();
@@ -1243,7 +1204,11 @@
                     }
                     if (band == PhysicalChannelConfig.BAND_UNKNOWN) {
                         mRil.riljLoge("Unsupported unknown band.");
-                        return;
+                        // TODO, b/288310456,
+                        //  If the band is unknown, PhysicalChannelConfig can be built without
+                        //  setBand. It should be enforced not to allow "unknown" bands in the
+                        //  near future.
+                        // return;
                     } else {
                         builder.setBand(band);
                     }
@@ -1277,28 +1242,6 @@
     }
 
     private void responseNetworkScan(int indicationType,
-            android.hardware.radio.V1_1.NetworkScanResult result) {
-        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
-
-        ArrayList<CellInfo> cellInfos =
-                RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
-        NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
-        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
-        mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
-    }
-
-    private void responseNetworkScan_1_2(int indicationType,
-            android.hardware.radio.V1_2.NetworkScanResult result) {
-        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
-
-        ArrayList<CellInfo> cellInfos =
-                RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
-        NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
-        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
-        mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
-    }
-
-    private void responseNetworkScan_1_4(int indicationType,
             android.hardware.radio.V1_4.NetworkScanResult result) {
         mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
diff --git a/src/java/com/android/internal/telephony/RadioMessagingProxy.java b/src/java/com/android/internal/telephony/RadioMessagingProxy.java
index 69ccf36..c652284 100644
--- a/src/java/com/android/internal/telephony/RadioMessagingProxy.java
+++ b/src/java/com/android/internal/telephony/RadioMessagingProxy.java
@@ -25,8 +25,8 @@
 import java.util.ArrayList;
 
 /**
- * A holder for IRadioMessaging. Use getHidl to get IRadio 1.0 and call the HIDL implementations or
- * getAidl to get IRadioMessaging and call the AIDL implementations of the HAL APIs.
+ * A holder for IRadioMessaging.
+ * Use getAidl to get IRadioMessaging and call the AIDL implementations of the HAL APIs.
  */
 public class RadioMessagingProxy extends RadioServiceProxy {
     private static final String TAG = "RadioMessagingProxy";
diff --git a/src/java/com/android/internal/telephony/RadioModemProxy.java b/src/java/com/android/internal/telephony/RadioModemProxy.java
index 4178293..cdcbcc0 100644
--- a/src/java/com/android/internal/telephony/RadioModemProxy.java
+++ b/src/java/com/android/internal/telephony/RadioModemProxy.java
@@ -20,8 +20,8 @@
 import android.telephony.Rlog;
 
 /**
- * A holder for IRadioModem. Use getHidl to get IRadio 1.0 and call the HIDL implementations or
- * getAidl to get IRadioModem and call the AIDL implementations of the HAL APIs.
+ * A holder for IRadioModem.
+ * Use getAidl to get IRadioModem and call the AIDL implementations of the HAL APIs.
  */
 public class RadioModemProxy extends RadioServiceProxy {
     private static final String TAG = "RadioModemProxy";
@@ -83,11 +83,11 @@
      * @throws RemoteException
      */
     public void enableModem(int serial, boolean on) throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_3)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             mModemProxy.enableModem(serial, on);
         } else {
-            ((android.hardware.radio.V1_3.IRadio) mRadioProxy).enableModem(serial, on);
+            mRadioProxy.enableModem(serial, on);
         }
     }
 
@@ -166,11 +166,11 @@
      * @throws RemoteException
      */
     public void getModemStackStatus(int serial) throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_3)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             mModemProxy.getModemStackStatus(serial);
         } else {
-            ((android.hardware.radio.V1_3.IRadio) mRadioProxy).getModemStackStatus(serial);
+            mRadioProxy.getModemStackStatus(serial);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/RadioNetworkProxy.java b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
index 246c2e0..5a56d23 100644
--- a/src/java/com/android/internal/telephony/RadioNetworkProxy.java
+++ b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
@@ -33,19 +33,17 @@
 import java.util.stream.Collectors;
 
 /**
- * A holder for IRadioNetwork. Use getHidl to get IRadio 1.0 and call the HIDL implementations or
- * getAidl to get IRadioNetwork and call the AIDL implementations of the HAL APIs.
+ * A holder for IRadioNetwork.
+ * Use getAidl to get IRadioNetwork and call the AIDL implementations of the HAL APIs.
  */
 public class RadioNetworkProxy extends RadioServiceProxy {
     private static final String TAG = "RadioNetworkProxy";
     private volatile android.hardware.radio.network.IRadioNetwork mNetworkProxy = null;
 
-    private static final int INDICATION_FILTERS_ALL_V1_0 =
+    private static final int INDICATION_FILTERS_ALL_V1_2 =
             android.hardware.radio.V1_5.IndicationFilter.SIGNAL_STRENGTH
                     | android.hardware.radio.V1_5.IndicationFilter.FULL_NETWORK_STATE
-                    | android.hardware.radio.V1_5.IndicationFilter.DATA_CALL_DORMANCY_CHANGED;
-    private static final int INDICATION_FILTERS_ALL_V1_2 =
-            INDICATION_FILTERS_ALL_V1_0
+                    | android.hardware.radio.V1_5.IndicationFilter.DATA_CALL_DORMANCY_CHANGED
                     | android.hardware.radio.V1_5.IndicationFilter.LINK_CAPACITY_ESTIMATE
                     | android.hardware.radio.V1_5.IndicationFilter.PHYSICAL_CHANNEL_CONFIG;
     private static final int INDICATION_FILTERS_ALL_V1_5 =
@@ -121,11 +119,8 @@
             mNetworkProxy.getAllowedNetworkTypesBitmap(serial);
         } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)) {
             ((android.hardware.radio.V1_6.IRadio) mRadioProxy).getAllowedNetworkTypesBitmap(serial);
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
-            ((android.hardware.radio.V1_4.IRadio) mRadioProxy)
-                    .getPreferredNetworkTypeBitmap(serial);
         } else {
-            mRadioProxy.getPreferredNetworkType(serial);
+            mRadioProxy.getPreferredNetworkTypeBitmap(serial);
         }
     }
 
@@ -278,10 +273,8 @@
             mNetworkProxy.getSignalStrength(serial);
         } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)) {
             ((android.hardware.radio.V1_6.IRadio) mRadioProxy).getSignalStrength_1_6(serial);
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
-            ((android.hardware.radio.V1_4.IRadio) mRadioProxy).getSignalStrength_1_4(serial);
         } else {
-            mRadioProxy.getSignalStrength(serial);
+            mRadioProxy.getSignalStrength_1_4(serial);
         }
     }
 
@@ -394,12 +387,8 @@
     public void setPreferredNetworkTypeBitmap(int serial, int networkTypesBitmask)
             throws RemoteException {
         if (isEmpty() || mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)) return;
-        if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
-            ((android.hardware.radio.V1_4.IRadio) mRadioProxy).setPreferredNetworkTypeBitmap(serial,
-                    RILUtils.convertToHalRadioAccessFamily(networkTypesBitmask));
-        } else {
-            mRadioProxy.setPreferredNetworkType(serial, networkTypesBitmask);
-        }
+        mRadioProxy.setPreferredNetworkTypeBitmap(serial,
+                RILUtils.convertToHalRadioAccessFamily(networkTypesBitmask));
     }
 
     /**
@@ -478,11 +467,8 @@
         } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
             ((android.hardware.radio.V1_5.IRadio) mRadioProxy).setIndicationFilter_1_5(serial,
                     filter & INDICATION_FILTERS_ALL_V1_5);
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_2)) {
-            ((android.hardware.radio.V1_2.IRadio) mRadioProxy).setIndicationFilter_1_2(serial,
-                    filter & INDICATION_FILTERS_ALL_V1_2);
         } else {
-            mRadioProxy.setIndicationFilter(serial, filter & INDICATION_FILTERS_ALL_V1_0);
+            mRadioProxy.setIndicationFilter_1_2(serial, filter & INDICATION_FILTERS_ALL_V1_2);
         }
     }
 
@@ -504,7 +490,7 @@
     public void setLinkCapacityReportingCriteria(int serial, int hysteresisMs, int hysteresisDlKbps,
             int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran)
             throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_2)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             mNetworkProxy.setLinkCapacityReportingCriteria(serial, hysteresisMs, hysteresisDlKbps,
                     hysteresisUlKbps, thresholdsDlKbps, thresholdsUlKbps,
@@ -519,7 +505,7 @@
             if (ran == AccessNetworkConstants.AccessNetworkType.NGRAN) {
                 throw new RuntimeException("NGRAN unsupported on IRadio version: " + mHalVersion);
             }
-            ((android.hardware.radio.V1_2.IRadio) mRadioProxy).setLinkCapacityReportingCriteria(
+            mRadioProxy.setLinkCapacityReportingCriteria(
                     serial, hysteresisMs, hysteresisDlKbps, hysteresisUlKbps,
                     RILUtils.primitiveArrayToArrayList(thresholdsDlKbps),
                     RILUtils.primitiveArrayToArrayList(thresholdsUlKbps),
@@ -603,7 +589,7 @@
      */
     public void setSignalStrengthReportingCriteria(int serial,
             @NonNull List<SignalThresholdInfo> signalThresholdInfos) throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_2)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             android.hardware.radio.network.SignalThresholdInfo[] halSignalThresholdsInfos =
             new android.hardware.radio.network.SignalThresholdInfo[signalThresholdInfos.size()];
@@ -622,14 +608,12 @@
             }
         } else {
             for (SignalThresholdInfo signalThresholdInfo : signalThresholdInfos) {
-                ((android.hardware.radio.V1_2.IRadio) mRadioProxy)
-                        .setSignalStrengthReportingCriteria(serial,
-                                signalThresholdInfo.getHysteresisMs(),
-                                signalThresholdInfo.getHysteresisDb(),
-                                RILUtils.primitiveArrayToArrayList(
-                                        signalThresholdInfo.getThresholds()),
-                                RILUtils.convertToHalAccessNetwork(
-                                        signalThresholdInfo.getRadioAccessNetworkType()));
+                mRadioProxy.setSignalStrengthReportingCriteria(serial,
+                        signalThresholdInfo.getHysteresisMs(),
+                        signalThresholdInfo.getHysteresisDb(),
+                        RILUtils.primitiveArrayToArrayList(signalThresholdInfo.getThresholds()),
+                        RILUtils.convertToHalAccessNetwork(
+                                signalThresholdInfo.getRadioAccessNetworkType()));
             }
         }
     }
@@ -657,7 +641,7 @@
      */
     public void setSystemSelectionChannels(int serial, List<RadioAccessSpecifier> specifiers)
             throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_3)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             mNetworkProxy.setSystemSelectionChannels(serial, !specifiers.isEmpty(),
                     specifiers.stream().map(RILUtils::convertToHalRadioAccessSpecifierAidl)
@@ -668,8 +652,8 @@
                             .map(RILUtils::convertToHalRadioAccessSpecifier15)
                             .collect(Collectors.toCollection(ArrayList::new)));
         } else {
-            ((android.hardware.radio.V1_3.IRadio) mRadioProxy).setSystemSelectionChannels(
-                    serial, !specifiers.isEmpty(), specifiers.stream()
+            mRadioProxy.setSystemSelectionChannels(serial, !specifiers.isEmpty(),
+                    specifiers.stream()
                             .map(RILUtils::convertToHalRadioAccessSpecifier11)
                             .collect(Collectors.toCollection(ArrayList::new)));
         }
@@ -684,7 +668,7 @@
      */
     public void startNetworkScan(int serial, NetworkScanRequest request,
             HalVersion overrideHalVersion, Message result) throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_1)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             android.hardware.radio.network.NetworkScanRequest halRequest =
                     new android.hardware.radio.network.NetworkScanRequest();
@@ -734,7 +718,7 @@
             }
             ((android.hardware.radio.V1_5.IRadio) mRadioProxy).startNetworkScan_1_5(
                     serial, halRequest);
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_2)) {
+        } else {
             android.hardware.radio.V1_2.NetworkScanRequest halRequest =
                     new android.hardware.radio.V1_2.NetworkScanRequest();
             halRequest.type = request.getScanType();
@@ -755,31 +739,7 @@
                 }
                 halRequest.specifiers.add(rasInHalFormat);
             }
-
-            if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
-                ((android.hardware.radio.V1_4.IRadio) mRadioProxy).startNetworkScan_1_4(
-                        serial, halRequest);
-            } else {
-                ((android.hardware.radio.V1_2.IRadio) mRadioProxy).startNetworkScan_1_2(
-                        serial, halRequest);
-            }
-        } else {
-            android.hardware.radio.V1_1.NetworkScanRequest halRequest =
-                    new android.hardware.radio.V1_1.NetworkScanRequest();
-            halRequest.type = request.getScanType();
-            halRequest.interval = request.getSearchPeriodicity();
-            for (RadioAccessSpecifier ras : request.getSpecifiers()) {
-                android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
-                        RILUtils.convertToHalRadioAccessSpecifier11(ras);
-                if (rasInHalFormat == null) {
-                    AsyncResult.forMessage(result, null,
-                            CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                    result.sendToTarget();
-                    return;
-                }
-                halRequest.specifiers.add(rasInHalFormat);
-            }
-            ((android.hardware.radio.V1_1.IRadio) mRadioProxy).startNetworkScan(serial, halRequest);
+            mRadioProxy.startNetworkScan_1_4(serial, halRequest);
         }
     }
 
@@ -789,11 +749,11 @@
      * @throws RemoteException
      */
     public void stopNetworkScan(int serial) throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_1)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             mNetworkProxy.stopNetworkScan(serial);
         } else {
-            ((android.hardware.radio.V1_1.IRadio) mRadioProxy).stopNetworkScan(serial);
+            mRadioProxy.stopNetworkScan(serial);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index 0bc2958..ada45ec 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -43,11 +43,9 @@
 import android.telephony.BarringInfo;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellInfo;
-import android.telephony.LinkCapacityEstimate;
 import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.NetworkScanRequest;
-import android.telephony.RadioAccessFamily;
 import android.telephony.RadioAccessSpecifier;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
@@ -103,7 +101,7 @@
      * @param cardStatus ICC card status as defined by CardStatus in types.hal
      */
     public void getIccCardStatusResponse(RadioResponseInfo responseInfo, CardStatus cardStatus) {
-        responseIccCardStatus(responseInfo, cardStatus);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -112,7 +110,7 @@
      */
     public void getIccCardStatusResponse_1_2(RadioResponseInfo responseInfo,
             android.hardware.radio.V1_2.CardStatus cardStatus) {
-        responseIccCardStatus_1_2(responseInfo, cardStatus);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -121,7 +119,7 @@
      */
     public void getIccCardStatusResponse_1_4(RadioResponseInfo responseInfo,
             android.hardware.radio.V1_4.CardStatus cardStatus) {
-        responseIccCardStatus_1_4(responseInfo, cardStatus);
+        responseIccCardStatus(responseInfo, cardStatus);
     }
 
     /**
@@ -208,7 +206,7 @@
      */
     public void getCurrentCallsResponse(RadioResponseInfo responseInfo,
             ArrayList<android.hardware.radio.V1_0.Call> calls) {
-        responseCurrentCalls(responseInfo, calls);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -217,7 +215,7 @@
      */
     public void getCurrentCallsResponse_1_2(RadioResponseInfo responseInfo,
             ArrayList<android.hardware.radio.V1_2.Call> calls) {
-        responseCurrentCalls_1_2(responseInfo, calls);
+        responseCurrentCalls(responseInfo, calls);
     }
 
     /**
@@ -301,27 +299,25 @@
 
     public void getSignalStrengthResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.V1_0.SignalStrength sigStrength) {
-        responseSignalStrength(responseInfo, sigStrength);
+        responseNotSupported(responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      * @param signalStrength Current signal strength of camped/connected cells
      */
-    public void getSignalStrengthResponse_1_2(
-            RadioResponseInfo responseInfo,
+    public void getSignalStrengthResponse_1_2(RadioResponseInfo responseInfo,
             android.hardware.radio.V1_2.SignalStrength signalStrength) {
-        responseSignalStrength_1_2(responseInfo, signalStrength);
+        responseNotSupported(responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      * @param signalStrength Current signal strength of camped/connected cells
      */
-    public void getSignalStrengthResponse_1_4(
-            RadioResponseInfo responseInfo,
+    public void getSignalStrengthResponse_1_4(RadioResponseInfo responseInfo,
             android.hardware.radio.V1_4.SignalStrength signalStrength) {
-        responseSignalStrength_1_4(responseInfo, signalStrength);
+        responseSignalStrength(responseInfo, signalStrength);
     }
 
     /**
@@ -341,14 +337,7 @@
      */
     public void getVoiceRegistrationStateResponse(RadioResponseInfo responseInfo,
             VoiceRegStateResult voiceRegResponse) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-
-        if (rr != null) {
-            if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, voiceRegResponse);
-            }
-            mRil.processResponseDone(rr, responseInfo, voiceRegResponse);
-        }
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -422,14 +411,7 @@
      */
     public void getDataRegistrationStateResponse(RadioResponseInfo responseInfo,
             DataRegStateResult dataRegResponse) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-
-        if (rr != null) {
-            if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, dataRegResponse);
-            }
-            mRil.processResponseDone(rr, responseInfo, dataRegResponse);
-        }
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -439,14 +421,7 @@
      */
     public void getDataRegistrationStateResponse_1_2(RadioResponseInfo responseInfo,
             android.hardware.radio.V1_2.DataRegStateResult dataRegResponse) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-
-        if (rr != null) {
-            if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, dataRegResponse);
-            }
-            mRil.processResponseDone(rr, responseInfo, dataRegResponse);
-        }
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -584,7 +559,7 @@
      */
     public void setupDataCallResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.V1_0.SetupDataCallResult setupDataCallResult) {
-        responseSetupDataCall(responseInfo, setupDataCallResult);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -812,7 +787,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void startNetworkScanResponse(RadioResponseInfo responseInfo) {
-        responseScanStatus(responseInfo, null /*fallbackHalVersion*/);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -900,7 +875,7 @@
      */
     public void getDataCallListResponse(RadioResponseInfo responseInfo,
             ArrayList<android.hardware.radio.V1_0.SetupDataCallResult> dataCallResultList) {
-        responseDataCallList(responseInfo, dataCallResultList);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -923,9 +898,6 @@
         responseDataCallList(responseInfo, dataCallResultList);
     }
 
-    public void sendOemRilRequestRawResponse(RadioResponseInfo responseInfo,
-            ArrayList<Byte> var2) {}
-
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
@@ -998,7 +970,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setPreferredNetworkTypeResponse(RadioResponseInfo responseInfo) {
-        responseVoid(responseInfo);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -1014,8 +986,7 @@
      * @param nwType RadioPreferredNetworkType defined in types.hal
      */
     public void getPreferredNetworkTypeResponse(RadioResponseInfo responseInfo, int nwType) {
-        mRil.mAllowedNetworkTypesBitmask = RadioAccessFamily.getRafFromNetworkType(nwType);
-        responseInts(responseInfo, RadioAccessFamily.getRafFromNetworkType(nwType));
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -1026,7 +997,6 @@
      */
     public void getPreferredNetworkTypeBitmapResponse(
             RadioResponseInfo responseInfo, int halRadioAccessFamilyBitmap) {
-
         int networkTypeBitmask = RILUtils.convertHalNetworkTypeBitMask(halRadioAccessFamilyBitmap);
         mRil.mAllowedNetworkTypesBitmask = networkTypeBitmask;
         responseInts(responseInfo, networkTypeBitmask);
@@ -1335,7 +1305,7 @@
 
     public void getCellInfoListResponse(RadioResponseInfo responseInfo,
             ArrayList<android.hardware.radio.V1_0.CellInfo> cellInfo) {
-        responseCellInfoList(responseInfo, cellInfo);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -1344,7 +1314,7 @@
      */
     public void getCellInfoListResponse_1_2(RadioResponseInfo responseInfo,
             ArrayList<android.hardware.radio.V1_2.CellInfo> cellInfo) {
-        responseCellInfoList(responseInfo, cellInfo);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -1453,8 +1423,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      * @param result IccIoResult as defined in types.hal
      */
-    public void iccTransmitApduLogicalChannelResponse(
-            RadioResponseInfo responseInfo,
+    public void iccTransmitApduLogicalChannelResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.V1_0.IccIoResult result) {
         responseIccIo(responseInfo, result);
     }
@@ -1502,8 +1471,10 @@
         responseVoid(responseInfo);
     }
 
-    public void getHardwareConfigResponse(
-            RadioResponseInfo responseInfo,
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void getHardwareConfigResponse(RadioResponseInfo responseInfo,
             ArrayList<android.hardware.radio.V1_0.HardwareConfig> config) {
         responseHardwareConfig(responseInfo, config);
     }
@@ -1577,7 +1548,7 @@
      * @param statusInfo LceStatusInfo indicating LCE status
      */
     public void startLceServiceResponse(RadioResponseInfo responseInfo, LceStatusInfo statusInfo) {
-        responseLceStatus(responseInfo, statusInfo);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -1585,11 +1556,11 @@
      * @param statusInfo LceStatusInfo indicating LCE status
      */
     public void stopLceServiceResponse(RadioResponseInfo responseInfo, LceStatusInfo statusInfo) {
-        responseLceStatus(responseInfo, statusInfo);
+        responseNotSupported(responseInfo);
     }
 
     public void pullLceDataResponse(RadioResponseInfo responseInfo, LceDataInfo lceInfo) {
-        responseLceData(responseInfo, lceInfo);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -1634,26 +1605,7 @@
      *        if Length of allowed carriers list is 0, numAllowed = 0.
      */
     public void setAllowedCarriersResponse(RadioResponseInfo responseInfo, int numAllowed) {
-        // The number of allowed carriers set correctly is not really useful. Even if one is
-        // missing, the operation has failed, as the list should be treated as a single
-        // configuration item. So, ignoring the value of numAllowed and considering only the
-        // value of the responseInfo.error.
-        int ret = TelephonyManager.SET_CARRIER_RESTRICTION_ERROR;
-        RILRequest rr = mRil.processResponse(responseInfo);
-        if (rr != null) {
-            mRil.riljLog("setAllowedCarriersResponse - error = " + responseInfo.error);
-
-            if (responseInfo.error == RadioError.NONE) {
-                ret = TelephonyManager.SET_CARRIER_RESTRICTION_SUCCESS;
-                sendMessageResponse(rr.mResult, ret);
-            } else if (responseInfo.error == RadioError.REQUEST_NOT_SUPPORTED) {
-                // Handle the case REQUEST_NOT_SUPPORTED as a valid response
-                responseInfo.error = RadioError.NONE;
-                ret = TelephonyManager.SET_CARRIER_RESTRICTION_NOT_SUPPORTED;
-                sendMessageResponse(rr.mResult, ret);
-            }
-            mRil.processResponseDone(rr, responseInfo, ret);
-        }
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -1681,13 +1633,7 @@
      */
     public void getAllowedCarriersResponse(RadioResponseInfo responseInfo, boolean allAllowed,
             CarrierRestrictions carriers) {
-        CarrierRestrictionsWithPriority carrierRestrictions = new CarrierRestrictionsWithPriority();
-        carrierRestrictions.allowedCarriers = carriers.allowedCarriers;
-        carrierRestrictions.excludedCarriers = carriers.excludedCarriers;
-        carrierRestrictions.allowedCarriersPrioritized = true;
-
-        responseCarrierRestrictions(responseInfo, allAllowed, carrierRestrictions,
-                SimLockMultiSimPolicy.NO_MULTISIM_POLICY);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -1696,11 +1642,36 @@
      * @param multiSimPolicy Policy for multi-sim devices.
      */
     public void getAllowedCarriersResponse_1_4(RadioResponseInfo responseInfo,
-            CarrierRestrictionsWithPriority carrierRestrictions,
-            int multiSimPolicy) {
-        // The API in IRadio 1.4 does not support the flag allAllowed, so setting it to false, so
-        // that values in carrierRestrictions are used.
-        responseCarrierRestrictions(responseInfo, false, carrierRestrictions, multiSimPolicy);
+            CarrierRestrictionsWithPriority carrierRestrictions, int multiSimPolicy) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+        if (rr == null) {
+            return;
+        }
+
+        int policy = CarrierRestrictionRules.MULTISIM_POLICY_NONE;
+        if (multiSimPolicy == SimLockMultiSimPolicy.ONE_VALID_SIM_MUST_BE_PRESENT) {
+            policy = CarrierRestrictionRules.MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT;
+        }
+
+        int carrierRestrictionDefault =
+                CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED;
+        if (!carrierRestrictions.allowedCarriersPrioritized) {
+            carrierRestrictionDefault = CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_ALLOWED;
+        }
+
+        CarrierRestrictionRules ret = CarrierRestrictionRules.newBuilder()
+                .setAllowedCarriers(RILUtils.convertHalCarrierList(
+                        carrierRestrictions.allowedCarriers))
+                .setExcludedCarriers(RILUtils.convertHalCarrierList(
+                        carrierRestrictions.excludedCarriers))
+                .setDefaultCarrierRestriction(carrierRestrictionDefault)
+                .setMultiSimPolicy(policy)
+                .build();
+
+        if (responseInfo.error == RadioError.NONE) {
+            sendMessageResponse(rr.mResult, ret);
+        }
+        mRil.processResponseDone(rr, responseInfo, ret);
     }
 
     /**
@@ -1735,7 +1706,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setSimCardPowerResponse(RadioResponseInfo responseInfo) {
-        responseVoid(responseInfo);
+        responseNotSupported(responseInfo);
     }
 
     /**
@@ -1874,39 +1845,7 @@
         }
     }
 
-    private void responseIccCardStatus(RadioResponseInfo responseInfo, CardStatus cardStatus) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-
-        if (rr != null) {
-            IccCardStatus iccCardStatus = RILUtils.convertHalCardStatus(cardStatus);
-            mRil.riljLog("responseIccCardStatus: from HIDL: " + iccCardStatus);
-            if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, iccCardStatus);
-            }
-            mRil.processResponseDone(rr, responseInfo, iccCardStatus);
-        }
-    }
-
-    private void responseIccCardStatus_1_2(RadioResponseInfo responseInfo,
-            android.hardware.radio.V1_2.CardStatus cardStatus) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-
-        if (rr != null) {
-            IccCardStatus iccCardStatus = RILUtils.convertHalCardStatus(cardStatus.base);
-            IccSlotPortMapping slotPortMapping = new IccSlotPortMapping();
-            slotPortMapping.mPhysicalSlotIndex = cardStatus.physicalSlotId;
-            iccCardStatus.mSlotPortMapping = slotPortMapping;
-            iccCardStatus.atr = cardStatus.atr;
-            iccCardStatus.iccid = cardStatus.iccid;
-            mRil.riljLog("responseIccCardStatus: from HIDL: " + iccCardStatus);
-            if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, iccCardStatus);
-            }
-            mRil.processResponseDone(rr, responseInfo, iccCardStatus);
-        }
-    }
-
-    private void responseIccCardStatus_1_4(RadioResponseInfo responseInfo,
+    private void responseIccCardStatus(RadioResponseInfo responseInfo,
             android.hardware.radio.V1_4.CardStatus cardStatus) {
         RILRequest rr = mRil.processResponse(responseInfo);
 
@@ -2035,52 +1974,12 @@
     }
 
     private void responseCurrentCalls(RadioResponseInfo responseInfo,
-            ArrayList<android.hardware.radio.V1_0.Call> calls) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-
-        if (rr != null) {
-            int num = calls.size();
-            ArrayList<DriverCall> dcCalls = new ArrayList<DriverCall>(num);
-            DriverCall dc;
-
-            for (int i = 0; i < num; i++) {
-                dc = RILUtils.convertToDriverCall(calls.get(i));
-
-                dcCalls.add(dc);
-
-                if (dc.isVoicePrivacy) {
-                    mRil.mVoicePrivacyOnRegistrants.notifyRegistrants();
-                    mRil.riljLog("InCall VoicePrivacy is enabled");
-                } else {
-                    mRil.mVoicePrivacyOffRegistrants.notifyRegistrants();
-                    mRil.riljLog("InCall VoicePrivacy is disabled");
-                }
-            }
-
-            Collections.sort(dcCalls);
-
-            if ((num == 0) && mRil.mTestingEmergencyCall.getAndSet(false)) {
-                if (mRil.mEmergencyCallbackModeRegistrant != null) {
-                    mRil.riljLog("responseCurrentCalls: call ended, testing emergency call,"
-                            + " notify ECM Registrants");
-                    mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
-                }
-            }
-
-            if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, dcCalls);
-            }
-            mRil.processResponseDone(rr, responseInfo, dcCalls);
-        }
-    }
-
-    private void responseCurrentCalls_1_2(RadioResponseInfo responseInfo,
             ArrayList<android.hardware.radio.V1_2.Call> calls) {
         RILRequest rr = mRil.processResponse(responseInfo);
 
         if (rr != null) {
             int num = calls.size();
-            ArrayList<DriverCall> dcCalls = new ArrayList<DriverCall>(num);
+            ArrayList<DriverCall> dcCalls = new ArrayList<>(num);
             DriverCall dc;
 
             for (int i = 0; i < num; i++) {
@@ -2155,6 +2054,15 @@
         }
     }
 
+    private void responseNotSupported(RadioResponseInfo responseInfo) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+        if (rr != null) {
+            mRil.riljLog(RILUtils.requestToString(rr.mRequest) + "not supported on IRadio < 1.4");
+            responseInfo.error = RadioError.REQUEST_NOT_SUPPORTED;
+            mRil.processResponseDone(rr, responseInfo, null);
+        }
+    }
+
     private void responseVoid(RadioResponseInfo responseInfo) {
         RILRequest rr = mRil.processResponse(responseInfo);
 
@@ -2301,34 +2209,6 @@
 
     private void responseSignalStrength(
             RadioResponseInfo responseInfo,
-            android.hardware.radio.V1_0.SignalStrength signalStrength) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-
-        if (rr != null) {
-            SignalStrength ret = RILUtils.convertHalSignalStrength(signalStrength);
-            if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, ret);
-            }
-            mRil.processResponseDone(rr, responseInfo, ret);
-        }
-    }
-
-    private void responseSignalStrength_1_2(
-            RadioResponseInfo responseInfo,
-            android.hardware.radio.V1_2.SignalStrength signalStrength) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-
-        if (rr != null) {
-            SignalStrength ret = RILUtils.convertHalSignalStrength(signalStrength);
-            if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, ret);
-            }
-            mRil.processResponseDone(rr, responseInfo, ret);
-        }
-    }
-
-    private void responseSignalStrength_1_4(
-            RadioResponseInfo responseInfo,
             android.hardware.radio.V1_4.SignalStrength signalStrength) {
         RILRequest rr = mRil.processResponse(responseInfo);
 
@@ -2696,69 +2576,6 @@
         }
     }
 
-    private void responseLceStatus(RadioResponseInfo responseInfo, LceStatusInfo statusInfo) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-
-        if (rr != null) {
-            ArrayList<Integer> ret = new ArrayList<>();
-            ret.add(statusInfo.lceStatus);
-            ret.add(Byte.toUnsignedInt(statusInfo.actualIntervalMs));
-            if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, ret);
-            }
-            mRil.processResponseDone(rr, responseInfo, ret);
-        }
-    }
-
-    private void responseLceData(RadioResponseInfo responseInfo, LceDataInfo lceInfo) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-
-        if (rr != null) {
-            List<LinkCapacityEstimate> ret = RILUtils.convertHalLceData(lceInfo);
-            if (responseInfo.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, ret);
-            }
-            mRil.processResponseDone(rr, responseInfo, ret);
-        }
-    }
-
-    private void responseCarrierRestrictions(RadioResponseInfo responseInfo, boolean allAllowed,
-            CarrierRestrictionsWithPriority carriers, int multiSimPolicy) {
-        RILRequest rr = mRil.processResponse(responseInfo);
-        if (rr == null) {
-            return;
-        }
-        CarrierRestrictionRules ret;
-
-        if (allAllowed) {
-            ret = CarrierRestrictionRules.newBuilder().setAllCarriersAllowed().build();
-        } else {
-            int policy = CarrierRestrictionRules.MULTISIM_POLICY_NONE;
-            if (multiSimPolicy == SimLockMultiSimPolicy.ONE_VALID_SIM_MUST_BE_PRESENT) {
-                policy = CarrierRestrictionRules.MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT;
-            }
-
-            int carrierRestrictionDefault =
-                    CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED;
-            if (!carriers.allowedCarriersPrioritized) {
-                carrierRestrictionDefault =
-                        CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_ALLOWED;
-            }
-
-            ret = CarrierRestrictionRules.newBuilder()
-                    .setAllowedCarriers(RILUtils.convertHalCarrierList(carriers.allowedCarriers))
-                    .setExcludedCarriers(RILUtils.convertHalCarrierList(carriers.excludedCarriers))
-                    .setDefaultCarrierRestriction(carrierRestrictionDefault)
-                    .setMultiSimPolicy(policy)
-                    .build();
-        }
-
-        if (responseInfo.error == RadioError.NONE) {
-            sendMessageResponse(rr.mResult, ret);
-        }
-        mRil.processResponseDone(rr, responseInfo, ret);
-    }
-
     /**
      * @param responseInfo Response info struct containing response type, serial number and error.
      */
diff --git a/src/java/com/android/internal/telephony/RadioSatelliteProxy.java b/src/java/com/android/internal/telephony/RadioSatelliteProxy.java
new file mode 100644
index 0000000..ec334b6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RadioSatelliteProxy.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.RemoteException;
+import android.telephony.Rlog;
+
+/**
+ * A holder for IRadioSatellite.
+ * Use getAidl to get IRadioSatellite and call the AIDL implementations of the HAL APIs.
+ */
+public class RadioSatelliteProxy extends RadioServiceProxy {
+    private static final String TAG = "RadioSatelliteProxy";
+    private volatile android.hardware.radio.satellite.IRadioSatellite mSatelliteProxy = null;
+
+    /**
+     * Sets IRadioSatellite as the AIDL implementation for RadioServiceProxy.
+     * @param halVersion Radio HAL version.
+     * @param satellite IRadioSatellite implementation.
+     *
+     * @return updated HAL version.
+     */
+    public HalVersion setAidl(HalVersion halVersion,
+            android.hardware.radio.satellite.IRadioSatellite satellite) {
+        HalVersion version = halVersion;
+        try {
+            version = RIL.getServiceHalVersion(satellite.getInterfaceVersion());
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "setAidl: " + e);
+        }
+        mHalVersion = version;
+        mSatelliteProxy = satellite;
+        mIsAidl = true;
+
+        Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+        return mHalVersion;
+    }
+
+    /**
+     * Gets the AIDL implementation of RadioSatelliteProxy.
+     * @return IRadioSatellite implementation.
+     */
+    public android.hardware.radio.satellite.IRadioSatellite getAidl() {
+        return mSatelliteProxy;
+    }
+
+    /**
+     * Resets RadioSatelliteProxy.
+     */
+    @Override
+    public void clear() {
+        super.clear();
+        mSatelliteProxy = null;
+    }
+
+    /**
+     * Checks whether a IRadioSatellite implementation exists.
+     * @return true if there is neither a HIDL nor AIDL implementation.
+     */
+    @Override
+    public boolean isEmpty() {
+        return mRadioProxy == null && mSatelliteProxy == null;
+    }
+
+    /**
+     * Call IRadioSatellite#responseAcknowledgement
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    @Override
+    public void responseAcknowledgement() throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.responseAcknowledgement();
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#getCapabilities
+     * @param serial Serial number of request.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void getCapabilities(int serial) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.getCapabilities(serial);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#setPower
+     * @param serial Serial number of request.
+     * @param on True for turning on.
+     *           False for turning off.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void setPower(int serial, boolean on) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.setPower(serial, on);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#getPowerState
+     * @param serial Serial number of request.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void getPowerState(int serial) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.getPowerState(serial);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#provisionService
+     * @param serial Serial number of request.
+     * @param imei IMEI of the SIM associated with the satellite modem.
+     * @param msisdn MSISDN of the SIM associated with the satellite modem.
+     * @param imsi IMSI of the SIM associated with the satellite modem.
+     * @param features List of features to be provisioned.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void provisionService(int serial, String imei, String msisdn, String imsi,
+            int[] features) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.provisionService(serial, imei, msisdn, imsi, features);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#addAllowedSatelliteContacts
+     * @param serial Serial number of request.
+     * @param contacts List of allowed contacts to be added.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void addAllowedSatelliteContacts(int serial, String[] contacts) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.addAllowedSatelliteContacts(serial, contacts);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#removeAllowedSatelliteContacts
+     * @param serial Serial number of request.
+     * @param contacts List of allowed contacts to be removed.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void removeAllowedSatelliteContacts(int serial, String[] contacts)
+            throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.removeAllowedSatelliteContacts(serial, contacts);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#sendMessages
+     * @param serial Serial number of request.
+     * @param messages List of messages in text format to be sent.
+     * @param destination The recipient of the message.
+     * @param latitude The current latitude of the device.
+     * @param longitude The current longitude of the device
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void sendMessages(int serial, String[] messages, String destination, double latitude,
+            double longitude) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.sendMessages(serial, messages, destination, latitude, longitude);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#getPendingMessages
+     * @param serial Serial number of request.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void getPendingMessages(int serial) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.getPendingMessages(serial);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#getSatelliteMode
+     * @param serial Serial number of request.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void getSatelliteMode(int serial) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.getSatelliteMode(serial);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#setIndicationFilter
+     * @param serial Serial number of request.
+     * @param filterBitmask The filter identifying what type of indication framework want to
+     *                         receive from modem.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void setIndicationFilter(int serial, int filterBitmask) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.setIndicationFilter(serial, filterBitmask);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#startSendingSatellitePointingInfo
+     * @param serial Serial number of request.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void startSendingSatellitePointingInfo(int serial) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.startSendingSatellitePointingInfo(serial);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#stopSendingSatellitePointingInfo
+     * @param serial Serial number of request.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void stopSendingSatellitePointingInfo(int serial) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.stopSendingSatellitePointingInfo(serial);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#getMaxCharactersPerTextMessage
+     * @param serial Serial number of request.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void getMaxCharactersPerTextMessage(int serial) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.getMaxCharactersPerTextMessage(serial);
+        }
+    }
+
+    /**
+     * Call IRadioSatellite#getTimeForNextSatelliteVisibility
+     * @param serial Serial number of request.
+     * @throws RemoteException Throws RemoteException when RadioSatellite service is not available.
+     */
+    public void getTimeForNextSatelliteVisibility(int serial) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mSatelliteProxy.getTimeForNextSatelliteVisibility(serial);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/RadioServiceProxy.java b/src/java/com/android/internal/telephony/RadioServiceProxy.java
index 4257327..02fc751 100644
--- a/src/java/com/android/internal/telephony/RadioServiceProxy.java
+++ b/src/java/com/android/internal/telephony/RadioServiceProxy.java
@@ -19,13 +19,13 @@
 import android.os.RemoteException;
 
 /**
- * A holder for IRadio services. Use getHidl to get IRadio 1.0 and call the HIDL implementations or
- * getAidl to get the AIDL service and call the AIDL implementations of the HAL APIs.
+ * A holder for IRadio services.
+ * Use getHidl to get the HIDL IRadio service or getAidl to get the corresponding AIDL service.
  */
 public abstract class RadioServiceProxy {
     boolean mIsAidl;
     HalVersion mHalVersion = RIL.RADIO_HAL_VERSION_UNKNOWN;
-    volatile android.hardware.radio.V1_0.IRadio mRadioProxy = null;
+    volatile android.hardware.radio.V1_4.IRadio mRadioProxy = null;
 
     /**
      * Whether RadioServiceProxy is an AIDL or HIDL implementation
@@ -40,7 +40,7 @@
      * @param halVersion Radio HAL version
      * @param radio      IRadio implementation
      */
-    public void setHidl(HalVersion halVersion, android.hardware.radio.V1_0.IRadio radio) {
+    public void setHidl(HalVersion halVersion, android.hardware.radio.V1_4.IRadio radio) {
         mHalVersion = halVersion;
         mRadioProxy = radio;
         mIsAidl = false;
@@ -50,7 +50,7 @@
      * Get the HIDL implementation of RadioServiceProxy
      * @return IRadio implementation
      */
-    public android.hardware.radio.V1_0.IRadio getHidl() {
+    public android.hardware.radio.V1_4.IRadio getHidl() {
         return mRadioProxy;
     }
 
diff --git a/src/java/com/android/internal/telephony/RadioSimProxy.java b/src/java/com/android/internal/telephony/RadioSimProxy.java
index 7c8ee7b..5265692 100644
--- a/src/java/com/android/internal/telephony/RadioSimProxy.java
+++ b/src/java/com/android/internal/telephony/RadioSimProxy.java
@@ -16,22 +16,17 @@
 
 package com.android.internal.telephony;
 
-import static com.android.internal.telephony.RILConstants.REQUEST_NOT_SUPPORTED;
-
-import android.os.AsyncResult;
-import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
 import com.android.internal.telephony.uicc.SimPhonebookRecord;
 
 /**
- * A holder for IRadioSim. Use getHidl to get IRadio 1.0 and call the HIDL implementations or
- * getAidl to get IRadioSim and call the AIDL implementations of the HAL APIs.
+ * A holder for IRadioSim.
+ * Use getAidl to get IRadioSim and call the AIDL implementations of the HAL APIs.
  */
 public class RadioSimProxy extends RadioServiceProxy {
     private static final String TAG = "RadioSimProxy";
@@ -160,10 +155,8 @@
         if (isEmpty()) return;
         if (isAidl()) {
             mSimProxy.getAllowedCarriers(serial);
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
-            ((android.hardware.radio.V1_4.IRadio) mRadioProxy).getAllowedCarriers_1_4(serial);
         } else {
-            mRadioProxy.getAllowedCarriers(serial);
+            mRadioProxy.getAllowedCarriers_1_4(serial);
         }
     }
 
@@ -521,11 +514,10 @@
      * Call IRadioSim#setAllowedCarriers
      * @param serial Serial number of request
      * @param carrierRestrictionRules Allowed carriers
-     * @param result Result to return in case of error
      * @throws RemoteException
      */
-    public void setAllowedCarriers(int serial, CarrierRestrictionRules carrierRestrictionRules,
-            Message result) throws RemoteException {
+    public void setAllowedCarriers(int serial, CarrierRestrictionRules carrierRestrictionRules)
+            throws RemoteException {
         if (isEmpty()) return;
         if (isAidl()) {
             // Prepare structure with allowed list, excluded list and priority
@@ -541,7 +533,7 @@
             mSimProxy.setAllowedCarriers(serial, carrierRestrictions,
                     RILUtils.convertToHalSimLockMultiSimPolicyAidl(
                             carrierRestrictionRules.getMultiSimPolicy()));
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
+        } else {
             // Prepare structure with allowed list, excluded list and priority
             android.hardware.radio.V1_4.CarrierRestrictionsWithPriority carrierRestrictions =
                     new android.hardware.radio.V1_4.CarrierRestrictionsWithPriority();
@@ -552,35 +544,9 @@
             carrierRestrictions.allowedCarriersPrioritized =
                     (carrierRestrictionRules.getDefaultCarrierRestriction()
                             == CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED);
-            ((android.hardware.radio.V1_4.IRadio) mRadioProxy).setAllowedCarriers_1_4(
-                    serial, carrierRestrictions, RILUtils.convertToHalSimLockMultiSimPolicy(
+            mRadioProxy.setAllowedCarriers_1_4(serial, carrierRestrictions,
+                    RILUtils.convertToHalSimLockMultiSimPolicy(
                             carrierRestrictionRules.getMultiSimPolicy()));
-        } else {
-            boolean isAllCarriersAllowed = carrierRestrictionRules.isAllCarriersAllowed();
-            boolean supported = (isAllCarriersAllowed
-                    || (carrierRestrictionRules.getExcludedCarriers().isEmpty()
-                    && (carrierRestrictionRules.getDefaultCarrierRestriction()
-                    == CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED)))
-                    && (RILUtils.convertToHalSimLockMultiSimPolicy(
-                    carrierRestrictionRules.getMultiSimPolicy())
-                    == android.hardware.radio.V1_4.SimLockMultiSimPolicy.NO_MULTISIM_POLICY);
-
-            if (!supported) {
-                // Feature is not supported by IRadio interface
-                if (result != null) {
-                    AsyncResult.forMessage(result, null,
-                            CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                    result.sendToTarget();
-                }
-                return;
-            }
-
-            // Prepare structure with allowed list
-            android.hardware.radio.V1_0.CarrierRestrictions carrierRestrictions =
-                    new android.hardware.radio.V1_0.CarrierRestrictions();
-            carrierRestrictions.allowedCarriers = RILUtils.convertToHalCarrierRestrictionList(
-                    carrierRestrictionRules.getAllowedCarriers());
-            mRadioProxy.setAllowedCarriers(serial, isAllCarriersAllowed, carrierRestrictions);
         }
     }
 
@@ -592,7 +558,7 @@
      */
     public void setCarrierInfoForImsiEncryption(int serial, ImsiEncryptionInfo imsiEncryptionInfo)
             throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_1)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             android.hardware.radio.sim.ImsiEncryptionInfo halImsiInfo =
                     new android.hardware.radio.sim.ImsiEncryptionInfo();
@@ -635,8 +601,7 @@
                 halImsiInfo.carrierKey.add(Byte.valueOf(b));
             }
 
-            ((android.hardware.radio.V1_1.IRadio) mRadioProxy).setCarrierInfoForImsiEncryption(
-                    serial, halImsiInfo);
+            mRadioProxy.setCarrierInfoForImsiEncryption(serial, halImsiInfo);
         }
     }
 
@@ -681,35 +646,16 @@
      * Call IRadioSim#setSimCardPower
      * @param serial Serial number of request
      * @param state SIM state (power down, power up, pass through)
-     * @param result Result to return in case of error
      * @throws RemoteException
      */
-    public void setSimCardPower(int serial, int state, Message result) throws RemoteException {
+    public void setSimCardPower(int serial, int state) throws RemoteException {
         if (isEmpty()) return;
         if (isAidl()) {
             mSimProxy.setSimCardPower(serial, state);
         } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)) {
             ((android.hardware.radio.V1_6.IRadio) mRadioProxy).setSimCardPower_1_6(serial, state);
-        } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_1)) {
-            ((android.hardware.radio.V1_1.IRadio) mRadioProxy).setSimCardPower_1_1(serial, state);
         } else {
-            switch (state) {
-                case TelephonyManager.CARD_POWER_DOWN: {
-                    mRadioProxy.setSimCardPower(serial, false);
-                    break;
-                }
-                case TelephonyManager.CARD_POWER_UP: {
-                    mRadioProxy.setSimCardPower(serial, true);
-                    break;
-                }
-                default: {
-                    if (result != null) {
-                        AsyncResult.forMessage(result, null,
-                                CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                        result.sendToTarget();
-                    }
-                }
-            }
+            mRadioProxy.setSimCardPower_1_1(serial, state);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/RadioVoiceProxy.java b/src/java/com/android/internal/telephony/RadioVoiceProxy.java
index 7f46424..e57a61d 100644
--- a/src/java/com/android/internal/telephony/RadioVoiceProxy.java
+++ b/src/java/com/android/internal/telephony/RadioVoiceProxy.java
@@ -24,8 +24,8 @@
 import java.util.ArrayList;
 
 /**
- * A holder for IRadioVoice. Use getHidl to get IRadio 1.0 and call the HIDL implementations or
- * getAidl to get IRadioVoice and call the AIDL implementations of the HAL APIs.
+ * A holder for IRadioVoice.
+ * Use getAidl to get IRadioVoice and call the AIDL implementations of the HAL APIs.
  */
 public class RadioVoiceProxy extends RadioServiceProxy {
     private static final String TAG = "RadioVoiceProxy";
@@ -153,7 +153,7 @@
     public void emergencyDial(int serial, String address, EmergencyNumber emergencyNumberInfo,
             boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo)
             throws RemoteException {
-        if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_4)) return;
+        if (isEmpty()) return;
         if (isAidl()) {
             mVoiceProxy.emergencyDial(serial,
                     RILUtils.convertToHalDialAidl(address, clirMode, uusInfo),
@@ -177,7 +177,7 @@
                     emergencyNumberInfo.getEmergencyNumberSourceBitmask()
                             == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
         } else {
-            ((android.hardware.radio.V1_4.IRadio) mRadioProxy).emergencyDial(serial,
+            mRadioProxy.emergencyDial(serial,
                     RILUtils.convertToHalDial(address, clirMode, uusInfo),
                     emergencyNumberInfo.getEmergencyServiceCategoryBitmaskInternalDial(),
                     emergencyNumberInfo.getEmergencyUrns() != null
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index a78242a..739ca8c 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -86,6 +86,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.analytics.TelephonyAnalytics;
+import com.android.internal.telephony.analytics.TelephonyAnalytics.SmsMmsAnalytics;
 import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
@@ -1048,6 +1050,19 @@
                     tracker.mMessageId,
                     tracker.isFromDefaultSmsApplication(mContext),
                     tracker.getInterval());
+            if (mPhone != null) {
+                TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+                if (telephonyAnalytics != null) {
+                    SmsMmsAnalytics smsMmsAnalytics = telephonyAnalytics.getSmsMmsAnalytics();
+                    if (smsMmsAnalytics != null) {
+                        smsMmsAnalytics.onOutgoingSms(
+                                tracker.mImsRetry > 0 /* isOverIms */,
+                                SmsManager.RESULT_ERROR_NONE
+                        );
+                    }
+                }
+            }
+
         } else {
             if (DBG) {
                 Rlog.d(TAG, "SMS send failed "
@@ -1084,6 +1099,19 @@
                         tracker.mMessageId,
                         tracker.isFromDefaultSmsApplication(mContext),
                         tracker.getInterval());
+                if (mPhone != null) {
+                    TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+                    if (telephonyAnalytics != null) {
+                        SmsMmsAnalytics smsMmsAnalytics = telephonyAnalytics.getSmsMmsAnalytics();
+                        if (smsMmsAnalytics != null) {
+                            smsMmsAnalytics.onOutgoingSms(
+                                    tracker.mImsRetry > 0 /* isOverIms */,
+                                    getNotInServiceError(ss)
+                            );
+                        }
+                    }
+                }
+
             } else if (error == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY
                     && tracker.mRetryCount < getMaxSmsRetryCount()) {
                 // Retry after a delay if needed.
@@ -1107,6 +1135,19 @@
                         tracker.mMessageId,
                         tracker.isFromDefaultSmsApplication(mContext),
                         tracker.getInterval());
+                if (mPhone != null) {
+                    TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+                    if (telephonyAnalytics != null) {
+                        SmsMmsAnalytics smsMmsAnalytics = telephonyAnalytics.getSmsMmsAnalytics();
+                        if (smsMmsAnalytics != null) {
+                            smsMmsAnalytics.onOutgoingSms(
+                                    tracker.mImsRetry > 0 /* isOverIms */,
+                                    SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY
+                            );
+                        }
+                    }
+                }
+
             } else {
                 int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE;
                 tracker.onFailed(mContext, error, errorCode);
@@ -1119,6 +1160,17 @@
                         tracker.mMessageId,
                         tracker.isFromDefaultSmsApplication(mContext),
                         tracker.getInterval());
+                if (mPhone != null) {
+                    TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+                    if (telephonyAnalytics != null) {
+                        SmsMmsAnalytics smsMmsAnalytics = telephonyAnalytics.getSmsMmsAnalytics();
+                        if (smsMmsAnalytics != null) {
+                            smsMmsAnalytics.onOutgoingSms(
+                                    tracker.mImsRetry > 0 /* isOverIms */,
+                                    error);
+                        }
+                    }
+                }
             }
         }
     }
@@ -2335,6 +2387,17 @@
                     trackers[0].mMessageId,
                     trackers[0].isFromDefaultSmsApplication(mContext),
                     trackers[0].getInterval());
+            if (mPhone != null) {
+                TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+                if (telephonyAnalytics != null) {
+                    SmsMmsAnalytics smsMmsAnalytics = telephonyAnalytics.getSmsMmsAnalytics();
+                    if (smsMmsAnalytics != null) {
+                        smsMmsAnalytics.onOutgoingSms(
+                                isIms(),
+                                error);
+                    }
+                }
+            }
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/SatelliteIndication.java b/src/java/com/android/internal/telephony/SatelliteIndication.java
new file mode 100644
index 0000000..2e561c3
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SatelliteIndication.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.TelephonyManager.HAL_SERVICE_SATELLITE;
+
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NEW_SATELLITE_MESSAGES;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PENDING_SATELLITE_MESSAGE_COUNT;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_MESSAGES_TRANSFER_COMPLETE;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_MODE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_POINTING_INFO_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_PROVISION_STATE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_RADIO_TECHNOLOGY_CHANGED;
+
+import android.hardware.radio.satellite.IRadioSatelliteIndication;
+import android.os.AsyncResult;
+import android.telephony.satellite.SatelliteDatagram;
+import android.util.Pair;
+
+/**
+ * Interface declaring unsolicited radio indications for Satellite APIs.
+ */
+public class SatelliteIndication extends IRadioSatelliteIndication.Stub {
+    private final RIL mRil;
+
+    public SatelliteIndication(RIL ril) {
+        mRil = ril;
+    }
+
+    @Override
+    public String getInterfaceHash() {
+        return IRadioSatelliteIndication.HASH;
+    }
+
+    @Override
+    public int getInterfaceVersion() {
+        return IRadioSatelliteIndication.VERSION;
+    }
+
+    /**
+     * Indicates that satellite has pending messages for the device to be pulled.
+     *
+     * @param indicationType Type of radio indication
+     * @param count Number of pending messages.
+     */
+    public void onPendingMessageCount(int indicationType, int count) {
+        mRil.processIndication(HAL_SERVICE_SATELLITE, indicationType);
+
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_PENDING_SATELLITE_MESSAGE_COUNT);
+
+        if (mRil.mPendingSatelliteMessageCountRegistrants != null) {
+            mRil.mPendingSatelliteMessageCountRegistrants.notifyRegistrants(
+                    new AsyncResult(null, count, null));
+        }
+    }
+
+    /**
+     * Indicates new message received on device.
+     *
+     * @param indicationType Type of radio indication
+     * @param messages List of new messages received.
+     */
+    public void onNewMessages(int indicationType, String[] messages) {
+        mRil.processIndication(HAL_SERVICE_SATELLITE, indicationType);
+
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_NEW_SATELLITE_MESSAGES);
+
+        if (mRil.mNewSatelliteMessagesRegistrants != null) {
+            for (int i = 0; i < messages.length; i++) {
+                SatelliteDatagram datagram = new SatelliteDatagram(messages[i].getBytes());
+                mRil.mNewSatelliteMessagesRegistrants.notifyRegistrants(
+                        new AsyncResult(null, new Pair<>(datagram, messages.length - i - 1), null));
+            }
+        }
+    }
+
+    /**
+     * Confirms that ongoing message transfer is complete.
+     *
+     * @param indicationType Type of radio indication
+     * @param complete True mean the transfer is complete.
+     *                 False means the transfer is not complete.
+     */
+    public void onMessagesTransferComplete(int indicationType, boolean complete) {
+        mRil.processIndication(HAL_SERVICE_SATELLITE, indicationType);
+
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SATELLITE_MESSAGES_TRANSFER_COMPLETE);
+
+        if (mRil.mSatelliteMessagesTransferCompleteRegistrants != null) {
+            mRil.mSatelliteMessagesTransferCompleteRegistrants.notifyRegistrants(
+                    new AsyncResult(null, complete, null));
+        }
+    }
+
+    /**
+     * Indicate that satellite Pointing input has changed.
+     *
+     * @param indicationType Type of radio indication
+     * @param pointingInfo The current pointing info.
+     */
+    public void onSatellitePointingInfoChanged(int indicationType,
+            android.hardware.radio.satellite.PointingInfo pointingInfo) {
+        mRil.processIndication(HAL_SERVICE_SATELLITE, indicationType);
+
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SATELLITE_POINTING_INFO_CHANGED);
+
+        if (mRil.mSatellitePointingInfoChangedRegistrants != null) {
+            mRil.mSatellitePointingInfoChangedRegistrants.notifyRegistrants(
+                    new AsyncResult(
+                            null,
+                            RILUtils.convertHalSatellitePointingInfo(pointingInfo),
+                            null));
+        }
+    }
+
+    /**
+     * Indicate that satellite mode has changed.
+     *
+     * @param indicationType Type of radio indication
+     * @param mode The current mode of the satellite modem.
+     */
+    public void onSatelliteModeChanged(int indicationType, int mode) {
+        mRil.processIndication(HAL_SERVICE_SATELLITE, indicationType);
+
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SATELLITE_MODE_CHANGED);
+
+        if (mRil.mSatelliteModeChangedRegistrants != null) {
+            mRil.mSatelliteModeChangedRegistrants.notifyRegistrants(
+                    new AsyncResult(null, mode, null));
+        }
+    }
+
+    /**
+     * Indicate that satellite radio technology has changed.
+     *
+     * @param indicationType Type of radio indication
+     * @param technology The current technology of the satellite modem.
+     */
+    public void onSatelliteRadioTechnologyChanged(int indicationType, int technology) {
+        mRil.processIndication(HAL_SERVICE_SATELLITE, indicationType);
+
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SATELLITE_RADIO_TECHNOLOGY_CHANGED);
+
+        if (mRil.mSatelliteRadioTechnologyChangedRegistrants != null) {
+            mRil.mSatelliteRadioTechnologyChangedRegistrants.notifyRegistrants(
+                    new AsyncResult(null, technology, null));
+        }
+    }
+
+    /**
+     * Indicate that satellite provision state has changed.
+     *
+     * @param indicationType Type of radio indication
+     * @param provisioned True means the service is provisioned.
+     *                    False means the service is not provisioned.
+     * @param features List of Feature whose provision state has changed.
+     */
+    public void onProvisionStateChanged(int indicationType, boolean provisioned, int[] features) {
+        // TODO: remove features and update AsyncResult
+        mRil.processIndication(HAL_SERVICE_SATELLITE, indicationType);
+
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SATELLITE_PROVISION_STATE_CHANGED);
+
+        if (mRil.mSatelliteProvisionStateChangedRegistrants != null) {
+            mRil.mSatelliteProvisionStateChangedRegistrants.notifyRegistrants(
+                    new AsyncResult(provisioned, null, null));
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/SatelliteResponse.java b/src/java/com/android/internal/telephony/SatelliteResponse.java
new file mode 100644
index 0000000..559691b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SatelliteResponse.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.TelephonyManager.HAL_SERVICE_SATELLITE;
+
+import android.hardware.radio.RadioError;
+import android.hardware.radio.RadioResponseInfo;
+import android.hardware.radio.satellite.IRadioSatelliteResponse;
+import android.telephony.satellite.SatelliteCapabilities;
+
+/**
+ * Interface declaring response functions to solicited radio requests for Satellite APIs.
+ */
+public class SatelliteResponse extends IRadioSatelliteResponse.Stub {
+    private final RIL mRil;
+
+    public SatelliteResponse(RIL ril) {
+        mRil = ril;
+    }
+
+    /**
+     * Acknowledge the receipt of radio request sent to the vendor. This must be sent only for
+     * radio request which take long time to respond.
+     * For more details, refer https://source.android.com/devices/tech/connect/ril.html
+     * @param serial Serial no. of the request whose acknowledgement is sent.
+     */
+    public void acknowledgeRequest(int serial) {
+        mRil.processRequestAck(serial);
+    }
+    /**
+     * Response of the request getCapabilities.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     * @param capabilities List of capabilities that the satellite modem supports.
+     */
+    public void getCapabilitiesResponse(RadioResponseInfo responseInfo,
+            android.hardware.radio.satellite.SatelliteCapabilities capabilities) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_SATELLITE, responseInfo);
+
+        if (rr != null) {
+            SatelliteCapabilities convertedSatelliteCapabilities =
+                    RILUtils.convertHalSatelliteCapabilities(capabilities);
+            if (responseInfo.error == RadioError.NONE) {
+                RadioResponse.sendMessageResponse(rr.mResult, convertedSatelliteCapabilities);
+            }
+            mRil.processResponseDone(rr, responseInfo, convertedSatelliteCapabilities);
+        }
+    }
+
+    /**
+     * Response of the request setPower.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     */
+    public void setPowerResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_SATELLITE, mRil, responseInfo);
+    }
+
+    /**
+     * Response of the request getPowerState.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     * @param on True means the modem is ON.
+     *           False means the modem is OFF.
+     */
+    public void getPowerStateResponse(RadioResponseInfo responseInfo, boolean on) {
+        RadioResponse.responseInts(HAL_SERVICE_SATELLITE, mRil, responseInfo, on ? 1 : 0);
+    }
+
+    /**
+     * Response of the request provisionService.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     * @param provisioned True means the service is provisioned.
+     *                    False means the service is not provisioned.
+     */
+    public void provisionServiceResponse(RadioResponseInfo responseInfo, boolean provisioned) {
+        RadioResponse.responseInts(HAL_SERVICE_SATELLITE, mRil, responseInfo, provisioned ? 1 : 0);
+    }
+
+    /**
+     * Response of the request addAllowedSatelliteContacts.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     */
+    public void addAllowedSatelliteContactsResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_SATELLITE, mRil, responseInfo);
+    }
+
+    /**
+     * Response of the request removeAllowedSatelliteContacts.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     */
+    public void removeAllowedSatelliteContactsResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_SATELLITE, mRil, responseInfo);
+    }
+
+    /**
+     * Response of the request sendMessages.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     */
+    public void sendMessagesResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_SATELLITE, mRil, responseInfo);
+    }
+
+    /**
+     * Response of the request getPendingMessages.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     * @param messages List of pending messages received.
+     */
+    public void getPendingMessagesResponse(RadioResponseInfo responseInfo, String[] messages) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_SATELLITE, responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                RadioResponse.sendMessageResponse(rr.mResult, messages);
+            }
+            mRil.processResponseDone(rr, responseInfo, messages);
+        }
+    }
+
+    /**
+     * Response of the request getSatelliteMode.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     * @param mode Current Mode of the satellite modem.
+     * @param technology The current technology of the satellite modem.
+     */
+    public void getSatelliteModeResponse(RadioResponseInfo responseInfo, int mode, int technology) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_SATELLITE, responseInfo);
+
+        if (rr != null) {
+            int[] ret = new int[]{mode, technology};
+            if (responseInfo.error == RadioError.NONE) {
+                RadioResponse.sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    /**
+     * Response of the request setIndicationFilter.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     */
+    public void setIndicationFilterResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_SATELLITE, mRil, responseInfo);
+    }
+
+    /**
+     * Response of the request startSendingSatellitePointingInfo.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     */
+    public void startSendingSatellitePointingInfoResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_SATELLITE, mRil, responseInfo);
+    }
+
+    /**
+     * Response of the request stopSendingSatellitePointingInfo.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     */
+    public void stopSendingSatellitePointingInfoResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_SATELLITE, mRil, responseInfo);
+    }
+
+    /**
+     * Response of the request getMaxCharactersPerTextMessage.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     * @param charLimit Maximum number of characters in a text message that can be sent.
+     */
+    public void getMaxCharactersPerTextMessageResponse(
+            RadioResponseInfo responseInfo, int charLimit) {
+        RadioResponse.responseInts(HAL_SERVICE_SATELLITE, mRil, responseInfo, charLimit);
+    }
+
+    /**
+     * Response of the request getTimeForNextSatelliteVisibility.
+     *
+     * @param responseInfo Response info struct containing serial no. and error
+     * @param timeInSeconds The duration in seconds after which the satellite will be visible.
+     */
+    public void getTimeForNextSatelliteVisibilityResponse(
+            RadioResponseInfo responseInfo, int timeInSeconds) {
+        RadioResponse.responseInts(HAL_SERVICE_SATELLITE, mRil, responseInfo, timeInSeconds);
+    }
+
+    @Override
+    public String getInterfaceHash() {
+        return IRadioSatelliteResponse.HASH;
+    }
+
+    @Override
+    public int getInterfaceVersion() {
+        return IRadioSatelliteResponse.VERSION;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 8a49670..94e2c5c 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -95,6 +95,7 @@
 import com.android.internal.telephony.data.DataNetwork;
 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
 import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.metrics.RadioPowerStateStats;
 import com.android.internal.telephony.metrics.ServiceStateStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.satellite.NtnCapabilityResolver;
@@ -685,7 +686,7 @@
         // system setting property AIRPLANE_MODE_ON is set in Settings.
         int airplaneMode = Settings.Global.getInt(mCr, Settings.Global.AIRPLANE_MODE_ON, 0);
         int enableCellularOnBoot = Settings.Global.getInt(mCr,
-                Settings.Global.ENABLE_CELLULAR_ON_BOOT, 1);
+                Settings.Global.ENABLE_CELLULAR_ON_BOOT, getDefaultEnableCellularOnBoot());
         mDesiredPowerState = (enableCellularOnBoot > 0) && ! (airplaneMode > 0);
         if (!mDesiredPowerState) {
             mRadioPowerOffReasons.add(TelephonyManager.RADIO_POWER_REASON_USER);
@@ -749,6 +750,11 @@
         }
     }
 
+    private int getDefaultEnableCellularOnBoot() {
+        return mPhone.getContext().getResources().getBoolean(
+            R.bool.config_enable_cellular_on_boot_default) ? 1 : 0;
+    }
+
     @VisibleForTesting
     public void updatePhoneType() {
 
@@ -1325,6 +1331,8 @@
                 break;
 
             case EVENT_RADIO_STATE_CHANGED:
+                RadioPowerStateStats.onRadioStateChanged(mCi.getRadioState());
+                // fall through, the code above only logs metrics when radio state changes
             case EVENT_PHONE_TYPE_SWITCHED:
                 if(!mPhone.isPhoneTypeGsm() &&
                         mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON) {
diff --git a/src/java/com/android/internal/telephony/SignalStrengthController.java b/src/java/com/android/internal/telephony/SignalStrengthController.java
index 8c35e57..383ffcd 100644
--- a/src/java/com/android/internal/telephony/SignalStrengthController.java
+++ b/src/java/com/android/internal/telephony/SignalStrengthController.java
@@ -30,6 +30,7 @@
 import android.os.RegistrantList;
 import android.os.RemoteException;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.AnomalyReporter;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellIdentity;
 import android.telephony.CellIdentityLte;
@@ -61,6 +62,7 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.PatternSyntaxException;
 
@@ -384,9 +386,10 @@
                 CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY);
         if (gsmRssiThresholds != null) {
             signalThresholdInfos.add(
-                    createSignalThresholdsInfo(
+                    validateAndCreateSignalThresholdInfo(
                             SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
                             gsmRssiThresholds,
+                            AccessNetworkThresholds.GERAN,
                             AccessNetworkConstants.AccessNetworkType.GERAN,
                             true));
         }
@@ -395,9 +398,10 @@
                 CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
         if (wcdmaRscpThresholds != null) {
             signalThresholdInfos.add(
-                    createSignalThresholdsInfo(
+                    validateAndCreateSignalThresholdInfo(
                             SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
                             wcdmaRscpThresholds,
+                            AccessNetworkThresholds.UTRAN,
                             AccessNetworkConstants.AccessNetworkType.UTRAN,
                             true));
         }
@@ -408,9 +412,10 @@
                 CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
         if (lteRsrpThresholds != null) {
             signalThresholdInfos.add(
-                    createSignalThresholdsInfo(
+                    validateAndCreateSignalThresholdInfo(
                             SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
                             lteRsrpThresholds,
+                            AccessNetworkThresholds.EUTRAN_RSRP,
                             AccessNetworkConstants.AccessNetworkType.EUTRAN,
                             (lteMeasurementEnabled & CellSignalStrengthLte.USE_RSRP) != 0));
         }
@@ -420,9 +425,10 @@
                     CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
             if (lteRsrqThresholds != null) {
                 signalThresholdInfos.add(
-                        createSignalThresholdsInfo(
+                        validateAndCreateSignalThresholdInfo(
                                 SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
                                 lteRsrqThresholds,
+                                AccessNetworkThresholds.EUTRAN_RSRQ,
                                 AccessNetworkConstants.AccessNetworkType.EUTRAN,
                                 (lteMeasurementEnabled & CellSignalStrengthLte.USE_RSRQ) != 0));
             }
@@ -431,9 +437,10 @@
                     CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
             if (lteRssnrThresholds != null) {
                 signalThresholdInfos.add(
-                        createSignalThresholdsInfo(
+                        validateAndCreateSignalThresholdInfo(
                                 SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
                                 lteRssnrThresholds,
+                                AccessNetworkThresholds.EUTRAN_RSSNR,
                                 AccessNetworkConstants.AccessNetworkType.EUTRAN,
                                 (lteMeasurementEnabled & CellSignalStrengthLte.USE_RSSNR) != 0));
             }
@@ -444,9 +451,10 @@
                     CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY);
             if (nrSsrsrpThresholds != null) {
                 signalThresholdInfos.add(
-                        createSignalThresholdsInfo(
+                        validateAndCreateSignalThresholdInfo(
                                 SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
                                 nrSsrsrpThresholds,
+                                AccessNetworkThresholds.NGRAN_SSRSRP,
                                 AccessNetworkConstants.AccessNetworkType.NGRAN,
                                 (nrMeasurementEnabled & CellSignalStrengthNr.USE_SSRSRP) != 0));
             }
@@ -455,9 +463,10 @@
                     CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY);
             if (nrSsrsrqThresholds != null) {
                 signalThresholdInfos.add(
-                        createSignalThresholdsInfo(
+                        validateAndCreateSignalThresholdInfo(
                                 SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
                                 nrSsrsrqThresholds,
+                                AccessNetworkThresholds.NGRAN_SSRSRQ,
                                 AccessNetworkConstants.AccessNetworkType.NGRAN,
                                 (nrMeasurementEnabled & CellSignalStrengthNr.USE_SSRSRQ) != 0));
             }
@@ -466,9 +475,10 @@
                     CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY);
             if (nrSssinrThresholds != null) {
                 signalThresholdInfos.add(
-                        createSignalThresholdsInfo(
+                        validateAndCreateSignalThresholdInfo(
                                 SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
                                 nrSssinrThresholds,
+                                AccessNetworkThresholds.NGRAN_SSSINR,
                                 AccessNetworkConstants.AccessNetworkType.NGRAN,
                                 (nrMeasurementEnabled & CellSignalStrengthNr.USE_SSSINR) != 0));
             }
@@ -477,9 +487,10 @@
                     CarrierConfigManager.KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY);
             if (wcdmaEcnoThresholds != null) {
                 signalThresholdInfos.add(
-                        createSignalThresholdsInfo(
+                        validateAndCreateSignalThresholdInfo(
                                 SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO,
                                 wcdmaEcnoThresholds,
+                                AccessNetworkThresholds.UTRAN_ECNO,
                                 AccessNetworkConstants.AccessNetworkType.UTRAN,
                                 false));
             }
@@ -1150,6 +1161,45 @@
     }
 
     /**
+     * Validate the provided signal {@code thresholds} info and fall back to use the
+     * {@code defaultThresholds} and report anomaly if invalid to prevent crashing Phone.
+     */
+    private static SignalThresholdInfo validateAndCreateSignalThresholdInfo(
+            int measurementType, @NonNull int[] thresholds, @NonNull int[] defaultThresholds,
+            int ran, boolean isEnabled) {
+        SignalThresholdInfo signalThresholdInfo;
+        try {
+            signalThresholdInfo = new SignalThresholdInfo.Builder()
+                    .setSignalMeasurementType(measurementType)
+                    .setThresholds(thresholds)
+                    .setRadioAccessNetworkType(ran)
+                    .setIsEnabled(isEnabled)
+                    .build();
+        // TODO(b/295236831): only catch IAE when phone global exception handler is introduced.
+        // Although SignalThresholdInfo only throws IAE for invalid carrier configs, we catch
+        // all exception to prevent crashing phone before global exception handler is available.
+        } catch (Exception e) {
+            signalThresholdInfo = new SignalThresholdInfo.Builder()
+                    .setSignalMeasurementType(measurementType)
+                    .setThresholds(defaultThresholds)
+                    .setRadioAccessNetworkType(ran)
+                    .setIsEnabled(isEnabled)
+                    .build();
+
+            AnomalyReporter.reportAnomaly(
+                    UUID.fromString("28232bc4-78ff-447e-b597-7c054c802407"),
+                    "Invalid parameter to generate SignalThresholdInfo: "
+                            + "measurementType=" + measurementType
+                            + ", thresholds=" + Arrays.toString(thresholds)
+                            + ", RAN=" + ran
+                            + ", isEnabled=" + isEnabled
+                            + ". Replaced with default thresholds: " + Arrays.toString(
+                            defaultThresholds));
+        }
+        return signalThresholdInfo;
+    }
+
+    /**
      * dBm thresholds that correspond to changes in signal strength indications.
      */
     private static final class AccessNetworkThresholds {
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index ecd6276..7fc499e 100644
--- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
+++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -31,6 +31,8 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 
+import com.android.internal.telephony.analytics.TelephonyAnalytics;
+import com.android.internal.telephony.analytics.TelephonyAnalytics.SmsMmsAnalytics;
 import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
 import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
@@ -242,6 +244,14 @@
                     if (phone != null) {
                         phone.getSmsStats().onDroppedIncomingMultipartSms(message.mIs3gpp2, rows,
                                 message.mMessageCount);
+                        TelephonyAnalytics telephonyAnalytics = phone.getTelephonyAnalytics();
+                        if (telephonyAnalytics != null) {
+                            SmsMmsAnalytics smsMmsAnalytics =
+                                    telephonyAnalytics.getSmsMmsAnalytics();
+                            if (smsMmsAnalytics != null) {
+                                smsMmsAnalytics.onDroppedIncomingMultipartSms();
+                            }
+                        }
                     }
                 }
             }
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index f7f24a2..17a4a54 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -40,6 +40,7 @@
 import com.android.internal.telephony.data.LinkBandwidthEstimator;
 import com.android.internal.telephony.data.PhoneSwitcher;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsNrSaModeHandler;
 import com.android.internal.telephony.imsphone.ImsPhone;
@@ -429,9 +430,10 @@
 
     public Phone makePhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
             int phoneId, int precisePhoneType,
-            TelephonyComponentFactory telephonyComponentFactory) {
+            TelephonyComponentFactory telephonyComponentFactory,
+            @NonNull FeatureFlags featureFlags) {
         return new GsmCdmaPhone(context, ci, notifier, phoneId, precisePhoneType,
-                telephonyComponentFactory);
+                telephonyComponentFactory, featureFlags);
     }
 
     public PhoneSwitcher makePhoneSwitcher(int maxDataAttachModemCount, Context context,
@@ -476,10 +478,12 @@
      *
      * @param phone The phone object
      * @param looper The looper for event handling
+     * @param featureFlags The feature flag.
      * @return The data network controller instance
      */
-    public DataNetworkController makeDataNetworkController(Phone phone, Looper looper) {
-        return new DataNetworkController(phone, looper);
+    public DataNetworkController makeDataNetworkController(Phone phone, Looper looper,
+            @NonNull FeatureFlags featureFlags) {
+        return new DataNetworkController(phone, looper, featureFlags);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/analytics/CallAnalyticsProvider.java b/src/java/com/android/internal/telephony/analytics/CallAnalyticsProvider.java
new file mode 100644
index 0000000..e0da0f1
--- /dev/null
+++ b/src/java/com/android/internal/telephony/analytics/CallAnalyticsProvider.java
@@ -0,0 +1,663 @@
+/*
+ * 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.internal.telephony.analytics;
+
+import static android.os.Build.VERSION.INCREMENTAL;
+
+import static com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.DATE_FORMAT;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.CallAnalyticsTable;
+import com.android.telephony.Rlog;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashMap;
+
+/**
+ * Provider class for calls, receives the data from CallAnalytics and performs business logic on the
+ * received data. It uses util class for required db operation. This class implements the
+ * TelephonyAnalyticsProvider interface to provide aggregation functionality.
+ */
+public class CallAnalyticsProvider implements TelephonyAnalyticsProvider {
+    private static final String TAG = CallAnalyticsProvider.class.getSimpleName();
+    private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00");
+    private TelephonyAnalyticsUtil mTelephonyAnalyticsUtil;
+    private String mDateOfDeletedRecordsCallTable;
+    private static final String CREATE_CALL_ANALYTICS_TABLE =
+            "CREATE TABLE IF NOT EXISTS "
+                    + CallAnalyticsTable.TABLE_NAME
+                    + "("
+                    + CallAnalyticsTable._ID
+                    + " INTEGER PRIMARY KEY,"
+                    + CallAnalyticsTable.LOG_DATE
+                    + " DATE ,"
+                    + CallAnalyticsTable.CALL_STATUS
+                    + " TEXT DEFAULT '',"
+                    + CallAnalyticsTable.CALL_TYPE
+                    + " TEXT DEFAULT '',"
+                    + CallAnalyticsTable.RAT
+                    + " TEXT DEFAULT '',"
+                    + CallAnalyticsTable.SLOT_ID
+                    + " INTEGER ,"
+                    + CallAnalyticsTable.FAILURE_REASON
+                    + " TEXT DEFAULT '',"
+                    + CallAnalyticsTable.RELEASE_VERSION
+                    + " TEXT DEFAULT '' , "
+                    + CallAnalyticsTable.COUNT
+                    + " INTEGER DEFAULT 1 "
+                    + ");";
+
+    private static final String[] CALL_INSERTION_PROJECTION = {
+        CallAnalyticsTable._ID, CallAnalyticsTable.COUNT
+    };
+
+    private static final String CALL_SUCCESS_INSERTION_SELECTION =
+            CallAnalyticsTable.CALL_TYPE
+                    + " = ? AND "
+                    + CallAnalyticsTable.LOG_DATE
+                    + " = ? AND "
+                    + CallAnalyticsTable.CALL_STATUS
+                    + " = ? AND "
+                    + CallAnalyticsTable.SLOT_ID
+                    + " = ? ";
+
+    private static final String CALL_FAILED_INSERTION_SELECTION =
+            CallAnalyticsTable.LOG_DATE
+                    + " = ? AND "
+                    + CallAnalyticsTable.CALL_STATUS
+                    + " = ? AND "
+                    + CallAnalyticsTable.CALL_TYPE
+                    + " = ? AND "
+                    + CallAnalyticsTable.SLOT_ID
+                    + " = ? AND "
+                    + CallAnalyticsTable.RAT
+                    + " = ? AND "
+                    + CallAnalyticsTable.FAILURE_REASON
+                    + " = ? AND "
+                    + CallAnalyticsTable.RELEASE_VERSION
+                    + " = ? ";
+
+    private static final String CALL_OLD_DATA_DELETION_SELECTION =
+            CallAnalyticsTable.LOG_DATE + " < ? ";
+
+    private static final String CALL_OVERFLOW_DATA_DELETION_SELECTION =
+            CallAnalyticsTable._ID
+                    + " IN "
+                    + " ( SELECT "
+                    + CallAnalyticsTable._ID
+                    + " FROM "
+                    + CallAnalyticsTable.TABLE_NAME
+                    + " ORDER BY "
+                    + CallAnalyticsTable.LOG_DATE
+                    + " DESC LIMIT -1 OFFSET ? )";
+
+    private enum CallStatus {
+        SUCCESS("Success"),
+        FAILURE("Failure");
+        public String value;
+
+        CallStatus(String value) {
+            this.value = value;
+        }
+    }
+
+    private enum CallType {
+        NORMAL("Normal Call"),
+        SOS("SOS Call");
+        public String value;
+
+        CallType(String value) {
+            this.value = value;
+        }
+    }
+
+    private final int mSlotIndex;
+
+    /**
+     * Initializes the CallAnalyticsProvider object and creates a table in the DB to log the
+     * information related to Calls.
+     *
+     * @param telephonyAnalyticsUtil : Util Class object to support db operations
+     * @param slotIndex : Logical slot index.
+     */
+    public CallAnalyticsProvider(TelephonyAnalyticsUtil telephonyAnalyticsUtil, int slotIndex) {
+        mTelephonyAnalyticsUtil = telephonyAnalyticsUtil;
+        mSlotIndex = slotIndex;
+        mTelephonyAnalyticsUtil.createTable(CREATE_CALL_ANALYTICS_TABLE);
+    }
+
+    private ContentValues getContentValues(
+            String callType, String callStatus, int slotId, String rat, String failureReason) {
+        ContentValues values = new ContentValues();
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        values.put(CallAnalyticsTable.LOG_DATE, dateToday);
+        values.put(CallAnalyticsTable.CALL_TYPE, callType);
+        values.put(CallAnalyticsTable.CALL_STATUS, callStatus);
+        values.put(CallAnalyticsTable.SLOT_ID, slotId);
+        values.put(CallAnalyticsTable.RAT, rat);
+        values.put(CallAnalyticsTable.FAILURE_REASON, failureReason);
+        values.put(CallAnalyticsTable.RELEASE_VERSION, INCREMENTAL);
+        return values;
+    }
+
+    private String[] getSuccessfulCallSelectionArgs(ContentValues values) {
+        return new String[] {
+            values.getAsString(CallAnalyticsTable.CALL_TYPE),
+            values.getAsString(CallAnalyticsTable.LOG_DATE),
+            CallStatus.SUCCESS.value,
+            values.getAsString(CallAnalyticsTable.SLOT_ID)
+        };
+    }
+
+    private String[] getFailedCallSelectionArgs(ContentValues values) {
+
+        return new String[] {
+            values.getAsString(CallAnalyticsTable.LOG_DATE),
+            values.getAsString(CallAnalyticsTable.CALL_STATUS),
+            values.getAsString(CallAnalyticsTable.CALL_TYPE),
+            values.getAsString(CallAnalyticsTable.SLOT_ID),
+            values.getAsString(CallAnalyticsTable.RAT),
+            values.getAsString(CallAnalyticsTable.FAILURE_REASON),
+            values.getAsString(CallAnalyticsTable.RELEASE_VERSION)
+        };
+    }
+
+    /**
+     * Receives data, processes it and sends for insertion to db.
+     *
+     * @param callType : Type of the Call , i.e. Normal or Sos
+     * @param callStatus : Defines call was success or failure
+     * @param slotId : Logical Slot index derived from Phone object.
+     * @param rat : Radio Access Technology on which call ended.
+     * @param failureReason : Failure Reason of the call.
+     */
+    public void insertDataToDb(
+            String callType, String callStatus, int slotId, String rat, String failureReason) {
+        ContentValues values = getContentValues(callType, callStatus, slotId, rat, failureReason);
+        Cursor cursor = null;
+        try {
+            if (values.getAsString(CallAnalyticsTable.CALL_STATUS)
+                    .equals(CallStatus.SUCCESS.value)) {
+                Rlog.d(TAG, "Insertion for Success Call");
+                String[] selectionArgs = getSuccessfulCallSelectionArgs(values);
+                cursor =
+                        mTelephonyAnalyticsUtil.getCursor(
+                                CallAnalyticsTable.TABLE_NAME,
+                                CALL_INSERTION_PROJECTION,
+                                CALL_SUCCESS_INSERTION_SELECTION,
+                                selectionArgs,
+                                null,
+                                null,
+                                null,
+                                null);
+            } else {
+
+                String[] selectionArgs = getFailedCallSelectionArgs(values);
+                cursor =
+                        mTelephonyAnalyticsUtil.getCursor(
+                                CallAnalyticsTable.TABLE_NAME,
+                                CALL_INSERTION_PROJECTION,
+                                CALL_FAILED_INSERTION_SELECTION,
+                                selectionArgs,
+                                null,
+                                null,
+                                null,
+                                null);
+            }
+            updateEntryIfExistsOrInsert(cursor, values);
+            deleteOldAndOverflowData();
+        } catch (Exception e) {
+            Rlog.e(TAG, "Error caught in insertDataToDb while insertion.");
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void updateEntryIfExistsOrInsert(Cursor cursor, ContentValues values) {
+        if (cursor != null && cursor.moveToFirst()) {
+            int idColumnIndex = cursor.getColumnIndex(CallAnalyticsTable._ID);
+            int countColumnIndex = cursor.getColumnIndex(CallAnalyticsTable.COUNT);
+            if (idColumnIndex != -1 && countColumnIndex != -1) {
+                int id = cursor.getInt(idColumnIndex);
+                int count = cursor.getInt(countColumnIndex);
+                int newCount = count + 1;
+
+                values.put(CallAnalyticsTable.COUNT, newCount);
+
+                String updateSelection = CallAnalyticsTable._ID + " = ? ";
+                String[] updateSelectionArgs = {String.valueOf(id)};
+                Rlog.d(TAG, "Updated Count = " + values.getAsString(CallAnalyticsTable.COUNT));
+
+                mTelephonyAnalyticsUtil.update(
+                        CallAnalyticsTable.TABLE_NAME,
+                        values,
+                        updateSelection,
+                        updateSelectionArgs);
+            }
+        } else {
+            Rlog.d(TAG, "Simple Insertion");
+            mTelephonyAnalyticsUtil.insert(CallAnalyticsTable.TABLE_NAME, values);
+        }
+    }
+
+    /** Gets the count stored in the cursor object. */
+    @VisibleForTesting
+    public long getCount(
+            String tableName,
+            String[] columns,
+            String selection,
+            String[] selectionArgs,
+            String groupBy,
+            String having,
+            String orderBy,
+            String limit) {
+        Cursor cursor = null;
+        long totalCount = 0;
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            CallAnalyticsTable.TABLE_NAME,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            groupBy,
+                            having,
+                            orderBy,
+                            limit);
+            totalCount = mTelephonyAnalyticsUtil.getCountFromCursor(cursor);
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return totalCount;
+    }
+
+    private String[] getColumnSelectionAndArgs(String callType, String callStatus) {
+        String selection;
+        String[] selectionAndArgs;
+        if (callType != null) {
+            if (callStatus != null) {
+                selection =
+                        CallAnalyticsTable.CALL_TYPE
+                                + " = ? AND "
+                                + CallAnalyticsTable.CALL_STATUS
+                                + " = ? AND "
+                                + CallAnalyticsTable.SLOT_ID
+                                + " = ? ";
+                selectionAndArgs =
+                        new String[] {
+                            selection, callType, callStatus, Integer.toString(mSlotIndex)
+                        };
+            } else {
+                selection =
+                        CallAnalyticsTable.CALL_TYPE
+                                + " = ? AND "
+                                + CallAnalyticsTable.SLOT_ID
+                                + " = ? ";
+                selectionAndArgs = new String[] {selection, callType, Integer.toString(mSlotIndex)};
+            }
+        } else {
+            if (callStatus != null) {
+                selection =
+                        CallAnalyticsTable.CALL_STATUS
+                                + " = ? AND "
+                                + CallAnalyticsTable.SLOT_ID
+                                + " = ? ";
+                selectionAndArgs =
+                        new String[] {selection, callStatus, Integer.toString(mSlotIndex)};
+            } else {
+                selection = CallAnalyticsTable.SLOT_ID + " = ? ";
+                selectionAndArgs = new String[] {selection, Integer.toString(mSlotIndex)};
+            }
+        }
+        return selectionAndArgs;
+    }
+
+    private long countCallsOfTypeAndStatus(String callType, String callStatus) {
+        int selectionIndex = 0;
+        String[] columns = {"sum(" + CallAnalyticsTable.COUNT + ")"};
+        String[] selectionAndArgs = getColumnSelectionAndArgs(callType, callStatus);
+        String selection = selectionAndArgs[selectionIndex];
+        int selectionArgsStartIndex = 1, selectionArgsEndIndex = selectionAndArgs.length;
+        String[] selectionArgs =
+                Arrays.copyOfRange(
+                        selectionAndArgs, selectionArgsStartIndex, selectionArgsEndIndex);
+        return getCount(
+                CallAnalyticsTable.TABLE_NAME,
+                columns,
+                selection,
+                selectionArgs,
+                null,
+                null,
+                null,
+                null);
+    }
+
+    private long countTotalCalls() {
+        return countCallsOfTypeAndStatus(null, null);
+    }
+
+    private long countFailedCalls() {
+        return countCallsOfTypeAndStatus(null, CallStatus.FAILURE.value);
+    }
+
+    private long countNormalCalls() {
+        return countCallsOfTypeAndStatus(CallType.NORMAL.value, null);
+    }
+
+    private long countFailedNormalCalls() {
+        return countCallsOfTypeAndStatus(CallType.NORMAL.value, CallStatus.FAILURE.value);
+    }
+
+    private long countSosCalls() {
+        return countCallsOfTypeAndStatus(CallType.SOS.value, null);
+    }
+
+    private long countFailedSosCalls() {
+        return countCallsOfTypeAndStatus(CallType.SOS.value, CallStatus.FAILURE.value);
+    }
+
+    private String getMaxFailureVersion() {
+        String[] columns = {CallAnalyticsTable.RELEASE_VERSION};
+        String selection =
+                CallAnalyticsTable.CALL_STATUS + " = ? AND " + CallAnalyticsTable.SLOT_ID + " = ? ";
+        String[] selectionArgs = {CallStatus.FAILURE.value, Integer.toString(mSlotIndex)};
+        String groupBy = CallAnalyticsTable.RELEASE_VERSION;
+        String orderBy = "SUM(" + CallAnalyticsTable.COUNT + ") DESC ";
+        String limit = "1";
+        Cursor cursor = null;
+        String version = "";
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            CallAnalyticsTable.TABLE_NAME,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            groupBy,
+                            null,
+                            orderBy,
+                            limit);
+
+            if (cursor != null && cursor.moveToFirst()) {
+                int releaseVersionColumnIndex =
+                        cursor.getColumnIndex(CallAnalyticsTable.RELEASE_VERSION);
+                if (releaseVersionColumnIndex != -1) {
+                    version = cursor.getString(releaseVersionColumnIndex);
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return version;
+    }
+
+    private String getMaxFailureNetworkType() {
+        String[] columns = {CallAnalyticsTable.RAT};
+        String selection =
+                CallAnalyticsTable.CALL_STATUS + " = ? AND " + CallAnalyticsTable.SLOT_ID + " = ? ";
+        String[] selectionArgs = {CallStatus.FAILURE.value, Integer.toString(mSlotIndex)};
+        String groupBy = CallAnalyticsTable.RAT;
+        String orderBy = "SUM(" + CallAnalyticsTable.COUNT + ") DESC ";
+        String limit = "1";
+        Cursor cursor = null;
+        String networkType = "";
+
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            CallAnalyticsTable.TABLE_NAME,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            groupBy,
+                            null,
+                            orderBy,
+                            limit);
+
+            if (cursor != null && cursor.moveToFirst()) {
+                int networkColumnIndex = cursor.getColumnIndex(CallAnalyticsTable.RAT);
+                networkType = cursor.getString(networkColumnIndex);
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+
+        return networkType;
+    }
+
+    private HashMap<String, Integer> getFailureCountByRatForCallType(String callType) {
+        return getFailureCountByColumnForCallType(CallAnalyticsTable.RAT, callType);
+    }
+
+    private HashMap<String, Integer> getFailureCountByReasonForCallType(String callType) {
+        return getFailureCountByColumnForCallType(CallAnalyticsTable.FAILURE_REASON, callType);
+    }
+
+    private HashMap<String, Integer> getFailureCountByColumnForCallType(
+            String column, String callType) {
+        String[] columns = {column, "SUM(" + CallAnalyticsTable.COUNT + ") AS count"};
+        String selection =
+                CallAnalyticsTable.CALL_TYPE
+                        + " = ? AND "
+                        + CallAnalyticsTable.CALL_STATUS
+                        + " = ? AND "
+                        + CallAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {callType, CallStatus.FAILURE.value, Integer.toString(mSlotIndex)};
+        String groupBy = column;
+        Cursor cursor = null;
+        HashMap<String, Integer> failureCountByReason = new HashMap<>();
+
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            CallAnalyticsTable.TABLE_NAME,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            groupBy,
+                            null,
+                            null,
+                            null);
+
+            if (cursor != null) {
+                int failureColumnIndex = cursor.getColumnIndex(column);
+                int failureCountColumnIndex = cursor.getColumnIndex("count");
+
+                if (failureCountColumnIndex != -1 && failureColumnIndex != -1) {
+                    while (cursor.moveToNext()) {
+                        String failureReason = cursor.getString(failureColumnIndex);
+                        int failureCount = cursor.getInt(failureCountColumnIndex);
+                        failureCountByReason.put(failureReason, failureCount);
+                    }
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+
+        return failureCountByReason;
+    }
+
+    protected void deleteOldAndOverflowData() {
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        if (mDateOfDeletedRecordsCallTable == null
+                || !mDateOfDeletedRecordsCallTable.equals(dateToday)) {
+            mTelephonyAnalyticsUtil.deleteOverflowAndOldData(
+                    CallAnalyticsTable.TABLE_NAME,
+                    CALL_OVERFLOW_DATA_DELETION_SELECTION,
+                    CALL_OLD_DATA_DELETION_SELECTION);
+            mDateOfDeletedRecordsCallTable = dateToday;
+        }
+    }
+
+    /** Setter function for mDateOfDeletedRecordsCallTable. */
+    public void setDateOfDeletedRecordsCallTable(String dateOfDeletedRecordsCallTable) {
+        mDateOfDeletedRecordsCallTable = dateOfDeletedRecordsCallTable;
+    }
+
+    private ArrayList<String> dumpInformationInList(
+            long totalCalls,
+            long failedCalls,
+            double percentageFailedCalls,
+            long normalCallsCount,
+            double percentageFailedNormalCalls,
+            long sosCallCount,
+            double percentageFailedSosCalls,
+            String maxFailureVersion,
+            String maxFailureNetworkType,
+            HashMap<String, Integer> failureCountByReasonNormalCall,
+            HashMap<String, Integer> failureCountByReasonSosCall,
+            HashMap<String, Integer> failureCountByRatNormalCall,
+            HashMap<String, Integer> failureCountByRatSosCall) {
+
+        ArrayList<String> aggregatedCallInformation = new ArrayList<>();
+        aggregatedCallInformation.add("Normal Call Stats");
+        aggregatedCallInformation.add("\tTotal Normal Calls = " + normalCallsCount);
+        aggregatedCallInformation.add(
+                "\tPercentage Failure of Normal Calls = "
+                        + DECIMAL_FORMAT.format(percentageFailedNormalCalls)
+                        + "%");
+        addFailureStatsFromHashMap(
+                failureCountByReasonNormalCall,
+                CallType.NORMAL.value,
+                normalCallsCount,
+                CallAnalyticsTable.FAILURE_REASON,
+                aggregatedCallInformation);
+        addFailureStatsFromHashMap(
+                failureCountByRatNormalCall,
+                CallType.NORMAL.value,
+                normalCallsCount,
+                CallAnalyticsTable.RAT,
+                aggregatedCallInformation);
+
+        if (sosCallCount > 0) {
+            aggregatedCallInformation.add("SOS Call Stats");
+            aggregatedCallInformation.add("\tTotal SOS Calls = " + sosCallCount);
+            aggregatedCallInformation.add(
+                    "\tPercentage Failure of SOS Calls = "
+                            + DECIMAL_FORMAT.format(percentageFailedSosCalls)
+                            + "%");
+            addFailureStatsFromHashMap(
+                    failureCountByReasonSosCall,
+                    CallType.SOS.value,
+                    sosCallCount,
+                    CallAnalyticsTable.FAILURE_REASON,
+                    aggregatedCallInformation);
+            addFailureStatsFromHashMap(
+                    failureCountByRatSosCall,
+                    CallType.SOS.value,
+                    sosCallCount,
+                    CallAnalyticsTable.RAT,
+                    aggregatedCallInformation);
+        }
+        if (failedCalls != 0) {
+            aggregatedCallInformation.add(
+                    "\tMax Call(Normal+SOS) Failures at Version : " + maxFailureVersion);
+            aggregatedCallInformation.add(
+                    "\tMax Call(Normal+SOS) Failures at Network Type: " + maxFailureNetworkType);
+        }
+
+        return aggregatedCallInformation;
+    }
+
+    private void addFailureStatsFromHashMap(
+            HashMap<String, Integer> failureCountByColumn,
+            String callType,
+            long totalCallsOfCallType,
+            String column,
+            ArrayList<String> aggregatedCallInformation) {
+        failureCountByColumn.forEach(
+                (k, v) -> {
+                    double percentageFail = (double) v / (double) totalCallsOfCallType * 100.0;
+                    aggregatedCallInformation.add(
+                            "\tNo. of "
+                                    + callType
+                                    + " failures at "
+                                    + column
+                                    + " : "
+                                    + k
+                                    + " = "
+                                    + v
+                                    + ", Percentage = "
+                                    + DECIMAL_FORMAT.format(percentageFail)
+                                    + "%");
+                });
+    }
+
+    /**
+     * Collects all information which is intended to be a part of the report by calling the required
+     * functions implemented in the class.
+     *
+     * @return List which contains all the Calls related information
+     */
+    public ArrayList<String> aggregate() {
+        long totalCalls = countTotalCalls();
+        long failedCalls = countFailedCalls();
+        double percentageFailedCalls = (double) failedCalls / (double) totalCalls * 100.0;
+        String maxFailuresVersion = getMaxFailureVersion();
+        String maxFailuresNetworkType = getMaxFailureNetworkType();
+        HashMap<String, Integer> failureCountByReasonNormalCall =
+                getFailureCountByReasonForCallType(CallType.NORMAL.value);
+        HashMap<String, Integer> failureCountByReasonSosCall =
+                getFailureCountByReasonForCallType(CallType.SOS.value);
+        HashMap<String, Integer> failureCountByRatNormalCall =
+                getFailureCountByRatForCallType(CallType.NORMAL.value);
+        HashMap<String, Integer> failureCountByRatSosCall =
+                getFailureCountByRatForCallType(CallType.SOS.value);
+        long normalCallsCount = countNormalCalls();
+        long normalCallFailureCount = countFailedNormalCalls();
+        double percentageFailureNormalCall =
+                (double) normalCallFailureCount / (double) normalCallsCount * 100.0;
+        long sosCallCount = countSosCalls();
+        long sosCallFailureCount = countFailedSosCalls();
+        double percentageFailureSosCall =
+                (double) sosCallFailureCount / (double) sosCallCount * 100.0;
+        ArrayList<String> aggregatedCallInformation =
+                dumpInformationInList(
+                        totalCalls,
+                        failedCalls,
+                        percentageFailedCalls,
+                        normalCallsCount,
+                        percentageFailureNormalCall,
+                        sosCallCount,
+                        percentageFailureSosCall,
+                        maxFailuresVersion,
+                        maxFailuresNetworkType,
+                        failureCountByReasonNormalCall,
+                        failureCountByReasonSosCall,
+                        failureCountByRatNormalCall,
+                        failureCountByRatSosCall);
+        return aggregatedCallInformation;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/analytics/ServiceStateAnalyticsProvider.java b/src/java/com/android/internal/telephony/analytics/ServiceStateAnalyticsProvider.java
new file mode 100644
index 0000000..cae44bd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/analytics/ServiceStateAnalyticsProvider.java
@@ -0,0 +1,428 @@
+/*
+ * 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.internal.telephony.analytics;
+
+import static android.os.Build.VERSION.INCREMENTAL;
+
+import static com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.DATE_FORMAT;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.analytics.TelephonyAnalytics.ServiceStateAnalytics.TimeStampedServiceState;
+import com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.ServiceStateAnalyticsTable;
+import com.android.telephony.Rlog;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashMap;
+
+/**
+ * Provider class for ServiceState. Receives the data from ServiceStateAnalytics. Performs business
+ * logic on the received data. Uses Util class for required db operation. Implements the
+ * TelephonyAnalyticsProvider interface to provide aggregation functionality.
+ */
+public class ServiceStateAnalyticsProvider implements TelephonyAnalyticsProvider {
+    protected TelephonyAnalyticsUtil mTelephonyAnalyticsUtil;
+    private static final String TAG = ServiceStateAnalyticsProvider.class.getSimpleName();
+    private static final String CREATE_SERVICE_STATE_TABLE_QUERY =
+            "CREATE TABLE IF NOT EXISTS "
+                    + ServiceStateAnalyticsTable.TABLE_NAME
+                    + " ( "
+                    + ServiceStateAnalyticsTable._ID
+                    + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+                    + ServiceStateAnalyticsTable.LOG_DATE
+                    + " DATE ,"
+                    + ServiceStateAnalyticsTable.SLOT_ID
+                    + " INTEGER , "
+                    + ServiceStateAnalyticsTable.TIME_DURATION
+                    + " INTEGER ,"
+                    + ServiceStateAnalyticsTable.RAT
+                    + " TEXT ,"
+                    + ServiceStateAnalyticsTable.DEVICE_STATUS
+                    + " TEXT ,"
+                    + ServiceStateAnalyticsTable.RELEASE_VERSION
+                    + " TEXT "
+                    + ");";
+
+    private static final String[] SERVICE_STATE_INSERTION_COLUMNS = {
+        ServiceStateAnalyticsTable._ID, ServiceStateAnalyticsTable.TIME_DURATION
+    };
+
+    private String mDateOfDeletedRecordsServiceStateTable;
+
+    private static final String SERVICE_STATE_INSERTION_SELECTION =
+            ServiceStateAnalyticsTable.LOG_DATE
+                    + " = ? AND "
+                    + ServiceStateAnalyticsTable.SLOT_ID
+                    + " = ? AND "
+                    + ServiceStateAnalyticsTable.RAT
+                    + " = ? AND "
+                    + ServiceStateAnalyticsTable.DEVICE_STATUS
+                    + " = ? AND "
+                    + ServiceStateAnalyticsTable.RELEASE_VERSION
+                    + " = ? ";
+
+    private static final String SERVICE_STATE_OVERFLOW_DATA_DELETION_SELECTION =
+            ServiceStateAnalyticsTable._ID
+                    + " IN "
+                    + " ( SELECT "
+                    + ServiceStateAnalyticsTable._ID
+                    + " FROM "
+                    + ServiceStateAnalyticsTable.TABLE_NAME
+                    + " ORDER BY "
+                    + ServiceStateAnalyticsTable.LOG_DATE
+                    + " DESC LIMIT -1 OFFSET ? )";
+
+    private static final String SERVICE_STATE_OLD_DATA_DELETION_SELECTION =
+            ServiceStateAnalyticsTable.LOG_DATE + " < ? ";
+
+    private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00");
+
+    private final int mSlotIndex;
+
+    /**
+     * Instantiates the ServiceStateAnalyticsProvider Object. Creates a table in the db for Storing
+     * ServiceState Related Information.
+     */
+    public ServiceStateAnalyticsProvider(TelephonyAnalyticsUtil databaseUtil, int slotIndex) {
+        mTelephonyAnalyticsUtil = databaseUtil;
+        mSlotIndex = slotIndex;
+        mTelephonyAnalyticsUtil.createTable(CREATE_SERVICE_STATE_TABLE_QUERY);
+    }
+
+    private ContentValues getContentValues(
+            TelephonyAnalytics.ServiceStateAnalytics.TimeStampedServiceState lastState,
+            long endTimeStamp) {
+        ContentValues values = new ContentValues();
+        long timeInterval = endTimeStamp - lastState.mTimestampStart;
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        values.put(ServiceStateAnalyticsTable.LOG_DATE, dateToday);
+        values.put(ServiceStateAnalyticsTable.TIME_DURATION, timeInterval);
+        values.put(ServiceStateAnalyticsTable.SLOT_ID, lastState.mSlotIndex);
+        values.put(ServiceStateAnalyticsTable.RAT, lastState.mRAT);
+        values.put(ServiceStateAnalyticsTable.DEVICE_STATUS, lastState.mDeviceStatus);
+        values.put(ServiceStateAnalyticsTable.RELEASE_VERSION, INCREMENTAL);
+
+        return values;
+    }
+
+    /** Receives the data, processes it and sends it for insertion to db. */
+    @VisibleForTesting
+    public void insertDataToDb(TimeStampedServiceState lastState, long endTimeStamp) {
+        ContentValues values = getContentValues(lastState, endTimeStamp);
+        Rlog.d(TAG, "  " + values.toString() + "Time = " + System.currentTimeMillis());
+        String[] selectionArgs = {
+            values.getAsString(ServiceStateAnalyticsTable.LOG_DATE),
+            values.getAsString(ServiceStateAnalyticsTable.SLOT_ID),
+            values.getAsString(ServiceStateAnalyticsTable.RAT),
+            values.getAsString(ServiceStateAnalyticsTable.DEVICE_STATUS),
+            values.getAsString(ServiceStateAnalyticsTable.RELEASE_VERSION)
+        };
+        Cursor cursor = null;
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            ServiceStateAnalyticsTable.TABLE_NAME,
+                            SERVICE_STATE_INSERTION_COLUMNS,
+                            SERVICE_STATE_INSERTION_SELECTION,
+                            selectionArgs,
+                            null,
+                            null,
+                            null,
+                            null);
+            updateIfEntryExistsOtherwiseInsert(cursor, values);
+            deleteOldAndOverflowData();
+
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void updateIfEntryExistsOtherwiseInsert(Cursor cursor, ContentValues values) {
+        if (cursor != null && cursor.moveToFirst()) {
+            int idIndex = cursor.getColumnIndex(ServiceStateAnalyticsTable._ID);
+            int timeDurationIndex = cursor.getColumnIndex(ServiceStateAnalyticsTable.TIME_DURATION);
+            if (idIndex != -1 && timeDurationIndex != -1) {
+                int id = cursor.getInt(idIndex);
+                int oldTimeDuration = cursor.getInt(timeDurationIndex);
+                int updatedTimeDuration =
+                        oldTimeDuration
+                                + Integer.parseInt(
+                                        values.getAsString(
+                                                ServiceStateAnalyticsTable.TIME_DURATION));
+                String updateSelection = ServiceStateAnalyticsTable._ID + " = ? ";
+                String[] updateSelectionArgs = {Integer.toString(id)};
+                values.put(ServiceStateAnalyticsTable.TIME_DURATION, updatedTimeDuration);
+                mTelephonyAnalyticsUtil.update(
+                        ServiceStateAnalyticsTable.TABLE_NAME,
+                        values,
+                        updateSelection,
+                        updateSelectionArgs);
+            }
+        } else {
+            mTelephonyAnalyticsUtil.insert(ServiceStateAnalyticsTable.TABLE_NAME, values);
+        }
+    }
+
+    private long getTotalUpTime() {
+        String[] columns = {"SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ")"};
+        String selection = ServiceStateAnalyticsTable.SLOT_ID + " = ? ";
+        String[] selectionArgs = {Integer.toString(mSlotIndex)};
+        Cursor cursor = null;
+        long duration = 0;
+
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            ServiceStateAnalyticsTable.TABLE_NAME,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            null,
+                            null,
+                            null,
+                            null);
+            if (cursor != null && cursor.moveToFirst()) {
+                duration = cursor.getLong(0);
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+
+        return duration;
+    }
+
+    private long outOfServiceDuration() {
+        String[] columns = {"SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ")"};
+        String selection =
+                ServiceStateAnalyticsTable.DEVICE_STATUS
+                        + " != ? "
+                        + " AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"IN_SERVICE", Integer.toString(mSlotIndex)};
+        long oosDuration = 0;
+        Cursor cursor = null;
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            ServiceStateAnalyticsTable.TABLE_NAME,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            null,
+                            null,
+                            null,
+                            null);
+            if (cursor != null && cursor.moveToFirst()) {
+                oosDuration = cursor.getLong(0);
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return oosDuration;
+    }
+
+    private HashMap<String, Long> getOutOfServiceDurationByReason() {
+        HashMap<String, Long> outOfServiceDurationByReason = new HashMap<>();
+        String[] columns = {
+            ServiceStateAnalyticsTable.DEVICE_STATUS,
+            "SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ") AS totalTime"
+        };
+        String selection =
+                ServiceStateAnalyticsTable.DEVICE_STATUS
+                        + " != ? AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"IN_SERVICE", Integer.toString(mSlotIndex)};
+        String groupBy = ServiceStateAnalyticsTable.DEVICE_STATUS;
+        Cursor cursor = null;
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            ServiceStateAnalyticsTable.TABLE_NAME,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            groupBy,
+                            null,
+                            null,
+                            null);
+
+            if (cursor != null) {
+                int reasonIndex = cursor.getColumnIndex(ServiceStateAnalyticsTable.DEVICE_STATUS);
+                int timeIndex = cursor.getColumnIndex("totalTime");
+
+                if (reasonIndex != -1 && timeIndex != -1) {
+                    while (cursor.moveToNext()) {
+                        String oosReason = cursor.getString(reasonIndex);
+                        long timeInterval = cursor.getLong(timeIndex);
+                        outOfServiceDurationByReason.put(oosReason, timeInterval);
+                    }
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return outOfServiceDurationByReason;
+    }
+
+    private HashMap<String, Long> getInServiceDurationByRat() {
+        HashMap<String, Long> inServiceDurationByRat = new HashMap<>();
+        String[] columns = {
+            ServiceStateAnalyticsTable.RAT,
+            "SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ") AS totalTime"
+        };
+        String selection =
+                ServiceStateAnalyticsTable.RAT
+                        + " != ? AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"NA", Integer.toString(mSlotIndex)};
+        String groupBy = ServiceStateAnalyticsTable.RAT;
+        Cursor cursor = null;
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            ServiceStateAnalyticsTable.TABLE_NAME,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            groupBy,
+                            null,
+                            null,
+                            null);
+
+            if (cursor != null) {
+                int ratIndex = cursor.getColumnIndex(ServiceStateAnalyticsTable.RAT);
+                int timeIndex = cursor.getColumnIndex("totalTime");
+
+                if (ratIndex != -1 && timeIndex != -1) {
+                    while (cursor.moveToNext()) {
+                        String rat = cursor.getString(ratIndex);
+                        long timeInterval = cursor.getLong(timeIndex);
+                        inServiceDurationByRat.put(rat, timeInterval);
+                    }
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+
+        return inServiceDurationByRat;
+    }
+
+    protected void deleteOldAndOverflowData() {
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        if (mDateOfDeletedRecordsServiceStateTable == null
+                || !mDateOfDeletedRecordsServiceStateTable.equals(dateToday)) {
+            mTelephonyAnalyticsUtil.deleteOverflowAndOldData(
+                    ServiceStateAnalyticsTable.TABLE_NAME,
+                    SERVICE_STATE_OVERFLOW_DATA_DELETION_SELECTION,
+                    SERVICE_STATE_OLD_DATA_DELETION_SELECTION);
+            mDateOfDeletedRecordsServiceStateTable = dateToday;
+        }
+    }
+
+    /** Setter function for mDateOfDeletedRecordsServiceStateTable */
+    public void setDateOfDeletedRecordsServiceStateTable(
+            String dateOfDeletedRecordsServiceStateTable) {
+        mDateOfDeletedRecordsServiceStateTable = dateOfDeletedRecordsServiceStateTable;
+    }
+
+    private ArrayList<String> dumpInformationInList(
+            Long upTime,
+            Long outOfServiceTime,
+            double percentageOutOfService,
+            HashMap<String, Long> oosByReason,
+            HashMap<String, Long> timeDurationByRat) {
+        ArrayList<String> aggregatedServiceStateInfo = new ArrayList<>();
+        aggregatedServiceStateInfo.add("Total UpTime = " + upTime + " millis");
+        aggregatedServiceStateInfo.add(
+                "Out of Service Time = "
+                        + outOfServiceTime
+                        + " millis, Percentage "
+                        + DECIMAL_FORMAT.format(percentageOutOfService)
+                        + "%");
+        oosByReason.forEach(
+                (k, v) -> {
+                    double percentageOosOfReason;
+                    if (upTime == 0) {
+                        percentageOosOfReason = 0.0;
+                    } else {
+                        percentageOosOfReason = (double) v / (double) upTime * 100.0;
+                    }
+                    aggregatedServiceStateInfo.add(
+                            "Out of service Reason = "
+                                    + k
+                                    + ", Percentage = "
+                                    + DECIMAL_FORMAT.format(percentageOosOfReason)
+                                    + "%");
+                });
+        timeDurationByRat.forEach(
+                (k, v) -> {
+                    double percentageInService;
+                    if (upTime == 0) {
+                        percentageInService = 0.0;
+                    } else {
+                        percentageInService = (double) v / (double) upTime * 100.0;
+                    }
+                    aggregatedServiceStateInfo.add(
+                            "IN_SERVICE RAT : "
+                                    + k
+                                    + ", Percentage = "
+                                    + DECIMAL_FORMAT.format(percentageInService)
+                                    + "%");
+                });
+        return aggregatedServiceStateInfo;
+    }
+
+    /**
+     * Collects all information which is intended to be a part of the report by calling the required
+     * functions implemented in the class.
+     *
+     * @return List which contains all the ServiceState related collected information.
+     */
+    public ArrayList<String> aggregate() {
+
+        long upTime = getTotalUpTime();
+        long outOfServiceTime = outOfServiceDuration();
+        double percentageOutOfService;
+        if (upTime == 0) {
+            percentageOutOfService = 0.0;
+        } else {
+            percentageOutOfService = (double) outOfServiceTime / upTime * 100.0;
+        }
+        HashMap<String, Long> oosByReason = getOutOfServiceDurationByReason();
+        HashMap<String, Long> timeDurationByRat = getInServiceDurationByRat();
+        return dumpInformationInList(
+                upTime, outOfServiceTime, percentageOutOfService, oosByReason, timeDurationByRat);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/analytics/SmsMmsAnalyticsProvider.java b/src/java/com/android/internal/telephony/analytics/SmsMmsAnalyticsProvider.java
new file mode 100644
index 0000000..e544765
--- /dev/null
+++ b/src/java/com/android/internal/telephony/analytics/SmsMmsAnalyticsProvider.java
@@ -0,0 +1,526 @@
+/*
+ * 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.internal.telephony.analytics;
+
+import static android.os.Build.VERSION.INCREMENTAL;
+
+import static com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.DATE_FORMAT;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable;
+import com.android.telephony.Rlog;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashMap;
+
+/**
+ * Provider class for Sms and Mms Receives the data from SmsMmsAnalytics Performs business logic on
+ * the received data Uses Util class for required db operation. Implements the
+ * TelephonyAnalyticsProvider interface to provide aggregation functionality.
+ */
+public class SmsMmsAnalyticsProvider implements TelephonyAnalyticsProvider {
+    private static final String TAG = SmsMmsAnalyticsProvider.class.getSimpleName();
+
+    private TelephonyAnalyticsUtil mTelephonyAnalyticsUtil;
+    private String mDateOfDeletedRecordsSmsMmsTable;
+
+    private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00");
+    private static final String CREATE_SMS_MMS_ANALYTICS_TABLE =
+            "CREATE TABLE IF NOT EXISTS "
+                    + SmsMmsAnalyticsTable.TABLE_NAME
+                    + "("
+                    + SmsMmsAnalyticsTable._ID
+                    + " INTEGER PRIMARY KEY,"
+                    + SmsMmsAnalyticsTable.LOG_DATE
+                    + " DATE ,"
+                    + SmsMmsAnalyticsTable.SMS_MMS_STATUS
+                    + " TEXT DEFAULT '',"
+                    + SmsMmsAnalyticsTable.SMS_MMS_TYPE
+                    + " TEXT DEFAULT '',"
+                    + SmsMmsAnalyticsTable.SLOT_ID
+                    + " INTEGER , "
+                    + SmsMmsAnalyticsTable.RAT
+                    + " TEXT DEFAULT '',"
+                    + SmsMmsAnalyticsTable.FAILURE_REASON
+                    + " TEXT DEFAULT '',"
+                    + SmsMmsAnalyticsTable.RELEASE_VERSION
+                    + " TEXT DEFAULT '' , "
+                    + SmsMmsAnalyticsTable.COUNT
+                    + " INTEGER DEFAULT 1 "
+                    + ");";
+    private static final String SMS_MMS_OVERFLOW_DATA_DELETION_SELECTION =
+            SmsMmsAnalyticsTable._ID
+                    + " IN "
+                    + " ( SELECT "
+                    + SmsMmsAnalyticsTable._ID
+                    + " FROM "
+                    + SmsMmsAnalyticsTable.TABLE_NAME
+                    + " ORDER BY "
+                    + SmsMmsAnalyticsTable.LOG_DATE
+                    + " DESC LIMIT -1 OFFSET ? ) ";
+    private static final String SMS_MMS_OLD_DATA_DELETION_SELECTION =
+            SmsMmsAnalyticsTable.LOG_DATE + " < ? ";
+
+    private enum SmsMmsType {
+        SMS_OUTGOING("SMS Outgoing"),
+        SMS_INCOMING("SMS Incoming"),
+        MMS_OUTGOING("MMS Outgoing"),
+        MMS_INCOMING("MMS Incoming");
+        public final String value;
+
+        SmsMmsType(String value) {
+            this.value = value;
+        }
+    }
+
+    private enum SmsMmsStatus {
+        SUCCESS("Success"),
+        FAILURE("Failure");
+        public final String value;
+
+        SmsMmsStatus(String value) {
+            this.value = value;
+        }
+    }
+
+    private final int mSlotIndex;
+
+    public SmsMmsAnalyticsProvider(TelephonyAnalyticsUtil databaseUtil, int slotIndex) {
+        mTelephonyAnalyticsUtil = databaseUtil;
+        mSlotIndex = slotIndex;
+        mTelephonyAnalyticsUtil.createTable(CREATE_SMS_MMS_ANALYTICS_TABLE);
+    }
+
+    private static final String[] SMS_MMS_INSERTION_PROJECTION = {
+            SmsMmsAnalyticsTable._ID, SmsMmsAnalyticsTable.COUNT
+    };
+
+    private static final String SMS_MMS_INSERTION_SUCCESS_SELECTION =
+            SmsMmsAnalyticsTable.LOG_DATE
+                    + " = ? AND "
+                    + SmsMmsAnalyticsTable.SMS_MMS_TYPE
+                    + " = ? AND "
+                    + SmsMmsAnalyticsTable.SMS_MMS_STATUS
+                    + " = ? AND "
+                    + SmsMmsAnalyticsTable.SLOT_ID
+                    + " = ? ";
+
+    private static final String SMS_MMS_INSERTION_FAILURE_SELECTION =
+            SmsMmsAnalyticsTable.LOG_DATE
+                    + " = ? AND "
+                    + SmsMmsAnalyticsTable.SMS_MMS_STATUS
+                    + " = ? AND "
+                    + SmsMmsAnalyticsTable.SMS_MMS_TYPE
+                    + " = ? AND "
+                    + SmsMmsAnalyticsTable.RAT
+                    + " = ? AND "
+                    + SmsMmsAnalyticsTable.SLOT_ID
+                    + " = ? AND "
+                    + SmsMmsAnalyticsTable.FAILURE_REASON
+                    + " = ? AND "
+                    + SmsMmsAnalyticsTable.RELEASE_VERSION
+                    + " = ? ";
+
+    private ContentValues getContentValues(
+            String status, String smsMmsType, String rat, String failureReason) {
+        ContentValues values = new ContentValues();
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        values.put(SmsMmsAnalyticsTable.LOG_DATE, dateToday);
+        values.put(SmsMmsAnalyticsTable.SMS_MMS_STATUS, status);
+        values.put(SmsMmsAnalyticsTable.SMS_MMS_TYPE, smsMmsType);
+        values.put(SmsMmsAnalyticsTable.RAT, rat);
+        values.put(SmsMmsAnalyticsTable.SLOT_ID, mSlotIndex);
+        values.put(SmsMmsAnalyticsTable.FAILURE_REASON, failureReason);
+        values.put(SmsMmsAnalyticsTable.RELEASE_VERSION, INCREMENTAL);
+        return values;
+    }
+
+    /**
+     * Processes the received data for insertion to the database.
+     *
+     * @param status : SMS Status ,i.e. Success or Failure
+     * @param smsMmsType : Type ,i.e. outgoing/incoming
+     * @param rat : Radio Access Technology
+     * @param failureReason : Reason for failure
+     */
+    @VisibleForTesting
+    public void insertDataToDb(String status, String smsMmsType, String rat, String failureReason) {
+        ContentValues values = getContentValues(status, smsMmsType, rat, failureReason);
+        Rlog.d(TAG, values.toString());
+        Cursor cursor = null;
+        String[] selectionArgs;
+        try {
+            if (values.getAsString(SmsMmsAnalyticsTable.SMS_MMS_STATUS)
+                    .equals(SmsMmsStatus.SUCCESS.value)) {
+                Rlog.d(TAG, "Success Entry Data for Sms/Mms: " + values.toString());
+                selectionArgs =
+                        new String[] {
+                                values.getAsString(SmsMmsAnalyticsTable.LOG_DATE),
+                                values.getAsString(SmsMmsAnalyticsTable.SMS_MMS_TYPE),
+                                values.getAsString(SmsMmsAnalyticsTable.SMS_MMS_STATUS),
+                                values.getAsString(SmsMmsAnalyticsTable.SLOT_ID)
+                        };
+                cursor =
+                        mTelephonyAnalyticsUtil.getCursor(
+                                SmsMmsAnalyticsTable.TABLE_NAME,
+                                SMS_MMS_INSERTION_PROJECTION,
+                                SMS_MMS_INSERTION_SUCCESS_SELECTION,
+                                selectionArgs,
+                                null,
+                                null,
+                                null,
+                                null);
+
+            } else {
+                selectionArgs =
+                        new String[] {
+                                values.getAsString(SmsMmsAnalyticsTable.LOG_DATE),
+                                values.getAsString(SmsMmsAnalyticsTable.SMS_MMS_STATUS),
+                                values.getAsString(SmsMmsAnalyticsTable.SMS_MMS_TYPE),
+                                values.getAsString(SmsMmsAnalyticsTable.RAT),
+                                values.getAsString(SmsMmsAnalyticsTable.SLOT_ID),
+                                values.getAsString(SmsMmsAnalyticsTable.FAILURE_REASON),
+                                values.getAsString(SmsMmsAnalyticsTable.RELEASE_VERSION)
+                        };
+                cursor =
+                        mTelephonyAnalyticsUtil.getCursor(
+                                SmsMmsAnalyticsTable.TABLE_NAME,
+                                SMS_MMS_INSERTION_PROJECTION,
+                                SMS_MMS_INSERTION_FAILURE_SELECTION,
+                                selectionArgs,
+                                null,
+                                null,
+                                null,
+                                null);
+            }
+            updateIfEntryExistsOtherwiseInsert(cursor, values);
+            deleteOldAndOverflowData();
+        } catch (Exception e) {
+            Rlog.e(TAG, "Exception during Sms/Mms Insertion [insertDataToDb()] " + e);
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+
+    }
+
+    /**
+     * Checks if a similar entry exists and updates the existing entry. Otherwise, inserts a new
+     * entry.
+     */
+    @VisibleForTesting
+    public void updateIfEntryExistsOtherwiseInsert(Cursor cursor, ContentValues values) {
+        if (cursor != null && cursor.moveToFirst()) {
+            int idColumnIndex = cursor.getColumnIndex(SmsMmsAnalyticsTable._ID);
+            int countColumnIndex = cursor.getColumnIndex(SmsMmsAnalyticsTable.COUNT);
+            if (idColumnIndex != -1 && countColumnIndex != -1) {
+                int id = cursor.getInt(idColumnIndex);
+                int count = cursor.getInt(countColumnIndex);
+                int newCount = count + 1;
+
+                values.put(SmsMmsAnalyticsTable.COUNT, newCount);
+
+                String updateSelection = SmsMmsAnalyticsTable._ID + " = ? ";
+                String[] updateSelectionArgs = {String.valueOf(id)};
+                Rlog.d(TAG, "Updated Count = " + values.getAsString(SmsMmsAnalyticsTable.COUNT));
+
+                mTelephonyAnalyticsUtil.update(
+                        SmsMmsAnalyticsTable.TABLE_NAME,
+                        values,
+                        updateSelection,
+                        updateSelectionArgs);
+            }
+        } else {
+            mTelephonyAnalyticsUtil.insert(SmsMmsAnalyticsTable.TABLE_NAME, values);
+        }
+    }
+
+    /** Gets the count from the cursor */
+    @VisibleForTesting
+    public long getCount(String[] columns, String selection, String[] selectionArgs) {
+        Cursor cursor = null;
+        long count = 0;
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            SmsMmsAnalyticsTable.TABLE_NAME,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            null,
+                            null,
+                            null,
+                            null);
+            count = mTelephonyAnalyticsUtil.getCountFromCursor(cursor);
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return count;
+    }
+
+    private long getSmsMmsOfTypeAndStatus(String smsMmsType, String smsMmsStatus) {
+        String[] columns = {"SUM(" + SmsMmsAnalyticsTable.COUNT + ")"};
+        String selection = SmsMmsAnalyticsTable.SLOT_ID + " = ? ";
+        String[] selectionArgs = {Integer.toString(mSlotIndex)};
+
+        if (smsMmsType != null && smsMmsStatus != null) {
+            selection =
+                    SmsMmsAnalyticsTable.SMS_MMS_TYPE
+                            + " = ? AND "
+                            + SmsMmsAnalyticsTable.SMS_MMS_STATUS
+                            + " = ? AND "
+                            + SmsMmsAnalyticsTable.SLOT_ID
+                            + " = ? ";
+            selectionArgs = new String[] {smsMmsType, smsMmsStatus, Integer.toString(mSlotIndex)};
+        } else if (smsMmsType != null) {
+            selection =
+                    SmsMmsAnalyticsTable.SMS_MMS_TYPE
+                            + " = ? AND "
+                            + SmsMmsAnalyticsTable.SLOT_ID
+                            + " = ? ";
+            selectionArgs = new String[] {smsMmsType, Integer.toString(mSlotIndex)};
+        } else if (smsMmsStatus != null) {
+            selection =
+                    SmsMmsAnalyticsTable.SMS_MMS_STATUS
+                            + " = ? AND "
+                            + SmsMmsAnalyticsTable.SLOT_ID
+                            + " = ? ";
+            selectionArgs = new String[] {smsMmsStatus, Integer.toString(mSlotIndex)};
+        }
+        return getCount(columns, selection, selectionArgs);
+    }
+
+    private long getSmsOutgoingCount() {
+        return getSmsMmsOfTypeAndStatus(SmsMmsType.SMS_OUTGOING.value, null);
+    }
+
+    private long getSmsIncomingCount() {
+        return getSmsMmsOfTypeAndStatus(SmsMmsType.SMS_INCOMING.value, null);
+    }
+
+    private long getMmsOutgoingCount() {
+        return getSmsMmsOfTypeAndStatus(SmsMmsType.MMS_OUTGOING.value, null);
+    }
+
+    private long getMmsIncomingCount() {
+        return getSmsMmsOfTypeAndStatus(SmsMmsType.MMS_INCOMING.value, null);
+    }
+
+    private long getFailedOutgoingSms() {
+        return getSmsMmsOfTypeAndStatus(SmsMmsType.SMS_OUTGOING.value, SmsMmsStatus.FAILURE.value);
+    }
+
+    private long getFailedIncomingSms() {
+        return getSmsMmsOfTypeAndStatus(SmsMmsType.SMS_INCOMING.value, SmsMmsStatus.FAILURE.value);
+    }
+
+    private long getFailedOutgoingMms() {
+        return getSmsMmsOfTypeAndStatus(SmsMmsType.MMS_OUTGOING.value, SmsMmsStatus.FAILURE.value);
+    }
+
+    private long getFailedIncomingMms() {
+        return getSmsMmsOfTypeAndStatus(SmsMmsType.MMS_INCOMING.value, SmsMmsStatus.FAILURE.value);
+    }
+
+    private HashMap<String, Integer> getSmsFailedCountByRat() {
+        HashMap<String, Integer> failedSmsTypeCountByRat = new HashMap<>();
+        String[] columns = {
+                SmsMmsAnalyticsTable.RAT, "SUM(" + SmsMmsAnalyticsTable.COUNT + ") as count "
+        };
+        String selection =
+                SmsMmsAnalyticsTable.SLOT_ID
+                        + " = ? AND "
+                        + SmsMmsAnalyticsTable.SMS_MMS_STATUS
+                        + " = ? AND "
+                        + SmsMmsAnalyticsTable.SMS_MMS_TYPE
+                        + " IN ('"
+                        + SmsMmsType.SMS_INCOMING.value
+                        + "', '"
+                        + SmsMmsType.SMS_OUTGOING.value
+                        + "' ) ";
+        String[] selectionArgs = {Integer.toString(mSlotIndex), SmsMmsStatus.FAILURE.value};
+        String groupBy = SmsMmsAnalyticsTable.RAT;
+        Cursor cursor = null;
+        try {
+            cursor =
+                    mTelephonyAnalyticsUtil.getCursor(
+                            SmsMmsAnalyticsTable.TABLE_NAME,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            groupBy,
+                            null,
+                            null,
+                            null);
+            if (cursor != null) {
+                int ratIndex = cursor.getColumnIndex(SmsMmsAnalyticsTable.RAT);
+                int countIndex = cursor.getColumnIndex("count");
+
+                if (ratIndex != -1 && countIndex != -1) {
+                    while (cursor.moveToNext()) {
+                        String rat = cursor.getString(ratIndex);
+                        int count = cursor.getInt(countIndex);
+                        failedSmsTypeCountByRat.put(rat, count);
+                    }
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return failedSmsTypeCountByRat;
+    }
+
+    protected void deleteOldAndOverflowData() {
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        if (mDateOfDeletedRecordsSmsMmsTable == null
+                || !mDateOfDeletedRecordsSmsMmsTable.equals(dateToday)) {
+            mTelephonyAnalyticsUtil.deleteOverflowAndOldData(
+                    SmsMmsAnalyticsTable.TABLE_NAME,
+                    SMS_MMS_OVERFLOW_DATA_DELETION_SELECTION,
+                    SMS_MMS_OLD_DATA_DELETION_SELECTION);
+            mDateOfDeletedRecordsSmsMmsTable = dateToday;
+        }
+    }
+
+    /** Setter function for mDateOfDeletedRecordsSmsMmsTable for testing purposes */
+    public void setDateOfDeletedRecordsSmsMmsTable(String deletedDate) {
+        mDateOfDeletedRecordsSmsMmsTable = deletedDate;
+    }
+
+    /**
+     * Sets all the received information in a list along with the associated String description.
+     *
+     * @return A list of all the information with their respective description.
+     */
+    @VisibleForTesting
+    public ArrayList<String> dumpInformationInList(
+            long totalOutgoingSms,
+            long totalIncomingSms,
+            long failedOutgoingSms,
+            long failedIncomingSms,
+            long totalOutgoingMms,
+            long totalIncomingMms,
+            double percentageFailureOutgoingSms,
+            double percentageFailureIncomingSms,
+            double percentageFailureOutgoingMms,
+            double percentageFailureIncomingMms,
+            HashMap<String, Integer> failedSmsTypeCountByRat) {
+
+        ArrayList<String> aggregatedSmsMmsInformation = new ArrayList<>();
+        aggregatedSmsMmsInformation.add("Total Outgoing Sms Count = " + totalOutgoingSms);
+        aggregatedSmsMmsInformation.add("Total Incoming Sms Count = " + totalIncomingSms);
+        aggregatedSmsMmsInformation.add(
+                "Failed Outgoing SMS Count = "
+                        + failedOutgoingSms
+                        + " Percentage failure rate for Outgoing SMS :"
+                        + DECIMAL_FORMAT.format(percentageFailureOutgoingSms)
+                        + "%");
+        aggregatedSmsMmsInformation.add(
+                "Failed Incoming SMS Count = "
+                        + failedIncomingSms
+                        + " Percentage failure rate for Incoming SMS :"
+                        + DECIMAL_FORMAT.format(percentageFailureIncomingSms)
+                        + "%");
+
+        double overallFailPercentage =
+                (double) (failedIncomingSms + failedOutgoingSms)
+                        / (double) (totalIncomingSms + totalOutgoingSms)
+                        * 100.0;
+        aggregatedSmsMmsInformation.add(
+                "Overall Fail Percentage = " + DECIMAL_FORMAT.format(overallFailPercentage) + "%");
+        try {
+            failedSmsTypeCountByRat.forEach(
+                    (k, v) -> {
+                        double percentageFail =
+                                ((double) v
+                                        / (double) (totalIncomingSms + totalOutgoingSms)
+                                        * 100.0);
+                        aggregatedSmsMmsInformation.add(
+                                "Failed SMS Count for RAT : "
+                                        + k
+                                        + " = "
+                                        + v
+                                        + ", Percentage = "
+                                        + DECIMAL_FORMAT.format(percentageFail)
+                                        + "%");
+                    });
+
+        } catch (Exception e) {
+            Rlog.d(TAG, "Exception in adding to List = " + e);
+        }
+
+        return aggregatedSmsMmsInformation;
+    }
+
+    /**
+     * Gathers all the necessary information for the report by using specific methods.
+     *
+     * @return List of SmsMms analytics information.
+     */
+    public ArrayList<String> aggregate() {
+        long totalOutgoingSms = getSmsOutgoingCount();
+        long totalIncomingSms = getSmsIncomingCount();
+        long totalOutgoingMms = getMmsOutgoingCount();
+        long totalIncomingMms = getMmsIncomingCount();
+        long totalFailedOutgoingSms = getFailedOutgoingSms();
+        long totalFailedIncomingSms = getFailedIncomingSms();
+        long totalFailedOutgoingMms = getFailedOutgoingMms();
+        long totalFailedIncomingMms = getFailedIncomingMms();
+        HashMap<String, Integer> failedSmsTypeCountByRat = getSmsFailedCountByRat();
+
+        double percentageFailureOutgoingSms =
+                totalOutgoingSms != 0
+                        ? (double) totalFailedOutgoingSms / totalOutgoingSms * 100.0
+                        : 0;
+        double percentageFailureIncomingSms =
+                totalIncomingSms != 0
+                        ? (double) totalFailedIncomingSms / totalIncomingSms * 100.0
+                        : 0;
+
+        double percentageFailureOutgoingMms =
+                totalOutgoingMms != 0
+                        ? (double) totalFailedOutgoingMms / totalOutgoingMms * 100.0
+                        : 0;
+        double percentageFailureIncomingMms =
+                totalIncomingMms != 0
+                        ? (double) totalFailedIncomingMms / totalIncomingMms * 100.0
+                        : 0;
+
+        return dumpInformationInList(
+                totalOutgoingSms,
+                totalIncomingSms,
+                totalFailedOutgoingSms,
+                totalFailedIncomingSms,
+                totalOutgoingMms,
+                totalIncomingMms,
+                percentageFailureOutgoingSms,
+                percentageFailureIncomingSms,
+                percentageFailureOutgoingMms,
+                percentageFailureIncomingMms,
+                failedSmsTypeCountByRat);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java b/src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java
new file mode 100644
index 0000000..d7a70be
--- /dev/null
+++ b/src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java
@@ -0,0 +1,1200 @@
+/*
+ * 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.internal.telephony.analytics;
+
+import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_IMS;
+import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_GENERIC;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_SUCCESS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.Annotation;
+import android.telephony.DisconnectCause;
+import android.telephony.ServiceState;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+import com.android.internal.telephony.InboundSmsHandler;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.telephony.Rlog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Class to handle all telephony analytics related operations Initializes all the Analytics,
+ * Provider and Util classes. Registers the required Callbacks for supporting the
+ * ServiceStateAnalytics , SMS Analytics and Call Analytics
+ */
+public class TelephonyAnalytics {
+    private static final String TAG = TelephonyAnalytics.class.getSimpleName();
+    protected static final int INVALID_SUB_ID = -1;
+    private final int mSlotIndex;
+    private final HandlerThread mHandlerThread;
+    private final Handler mHandler;
+    private ExecutorService mExecutorService;
+    protected TelephonyAnalyticsUtil mTelephonyAnalyticsUtil;
+    protected int mSubId;
+    protected ServiceStateAnalytics mServiceStateAnalytics;
+    protected Context mContext;
+    protected Executor mExecutor;
+    protected SubscriptionManager mSubscriptionManager;
+    protected final SubscriptionManager.OnSubscriptionsChangedListener
+            mSubscriptionsChangeListener =
+            new SubscriptionManager.OnSubscriptionsChangedListener() {
+                @Override
+                public void onSubscriptionsChanged() {
+                    int newSubId = getSubId();
+                    if ((mSubId != newSubId)
+                            && (newSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID)) {
+                        stopAnalytics(mSubId);
+                        mSubId = newSubId;
+                        startAnalytics(newSubId);
+                        Rlog.d(
+                                TAG,
+                                "Started Listener, mSubId = "
+                                        + mSubId
+                                        + "SlotId = "
+                                        + mSlotIndex);
+                    }
+                }
+            };
+    protected CallAnalyticsProvider mCallAnalyticsProvider;
+    protected SmsMmsAnalyticsProvider mSmsMmsAnalyticsProvider;
+    protected ServiceStateAnalyticsProvider mServiceStateAnalyticsProvider;
+    protected SmsMmsAnalytics mSmsMmsAnalytics;
+    protected CallAnalytics mCallAnalytics;
+    protected Phone mPhone;
+
+    public TelephonyAnalytics(Phone phone) {
+        mPhone = phone;
+        mContext = mPhone.getContext();
+        mExecutor = Runnable::run;
+        mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+        mSlotIndex = mPhone.getPhoneId();
+
+        mHandlerThread = new HandlerThread(TelephonyAnalytics.class.getSimpleName());
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mExecutorService = Executors.newSingleThreadExecutor();
+        mTelephonyAnalyticsUtil = TelephonyAnalyticsUtil.getInstance(mContext);
+        initializeAnalyticsClasses();
+        mCallAnalyticsProvider = new CallAnalyticsProvider(mTelephonyAnalyticsUtil, mSlotIndex);
+        mSmsMmsAnalyticsProvider = new SmsMmsAnalyticsProvider(mTelephonyAnalyticsUtil, mSlotIndex);
+        mServiceStateAnalyticsProvider =
+                new ServiceStateAnalyticsProvider(mTelephonyAnalyticsUtil, mSlotIndex);
+
+        startAnalytics(mSubId);
+
+        if (mSubscriptionManager != null) {
+            mSubscriptionManager.addOnSubscriptionsChangedListener(
+                    mExecutor, mSubscriptionsChangeListener);
+            Rlog.d(TAG, "stopped listener");
+        }
+    }
+
+    @SuppressLint("MissingPermission")
+    private int getSubId() {
+        int subId = INVALID_SUB_ID;
+        try {
+            SubscriptionInfo info =
+                    mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(mSlotIndex);
+            subId = info.getSubscriptionId();
+            Rlog.d("TelephonyAnalyticsSubId", "SubId = " + subId
+                    + "SlotIndex = " + mSlotIndex);
+        } catch (NullPointerException e) {
+            Rlog.e("TelephonyAnalyticsSubId", "Null Pointer Exception Caught");
+        }
+        return subId;
+    }
+
+    private void initializeAnalyticsClasses() {
+        mServiceStateAnalytics = new ServiceStateAnalytics(mExecutor);
+        mSmsMmsAnalytics = new SmsMmsAnalytics();
+        mCallAnalytics = new CallAnalytics();
+    }
+
+    protected void startAnalytics(int subId) {
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            Rlog.d(
+                    "StartAnalytics",
+                    "Invalid SubId = " + SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            return;
+        }
+        mServiceStateAnalytics.registerMyListener(mContext, subId);
+    }
+
+    protected void stopAnalytics(int subId) {
+        if (mServiceStateAnalytics != null) {
+            mServiceStateAnalytics.unregisterMyListener(subId);
+        }
+    }
+
+    public SmsMmsAnalytics getSmsMmsAnalytics() {
+        return mSmsMmsAnalytics;
+    }
+
+    public CallAnalytics getCallAnalytics() {
+        return mCallAnalytics;
+    }
+
+    /**
+     * Uses the provider class objects,collects the aggregated report from the respective provider
+     * classes. Dumps the collected stats in the bugreport.
+     */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+        pw.println("+    Telephony Analytics Report [2 months] [Slot ID = " + mSlotIndex + "]  +");
+        pw.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+        pw.println("Call Analytics Summary");
+        ArrayList<String> aggregatedCallInfo = mCallAnalyticsProvider.aggregate();
+        for (String info : aggregatedCallInfo) {
+            pw.println("\t" + info);
+        }
+        pw.println("-----------------------------------------------");
+        pw.println("SMS/MMS Analytics Summary");
+        ArrayList<String> aggregatedSmsMmsInfo = mSmsMmsAnalyticsProvider.aggregate();
+        for (String info : aggregatedSmsMmsInfo) {
+            pw.println("\t\t" + info);
+        }
+        pw.println("-----------------------------------------------");
+        mServiceStateAnalytics.recordCurrentStateBeforeDump();
+        pw.println("Service State Analytics Summary ");
+        ArrayList<String> aggregatedServiceStateInfo = mServiceStateAnalyticsProvider.aggregate();
+        for (String info : aggregatedServiceStateInfo) {
+            pw.println("\t\t" + info);
+        }
+        pw.println("-----------------------------------------------");
+    }
+
+    /**
+     * Provides implementation for processing received Call related data. It implements functions to
+     * handle various scenarios pertaining to Calls. Passes the data to its provider class
+     * for further processing.
+     */
+    public class CallAnalytics {
+        private static final String TAG = CallAnalytics.class.getSimpleName();
+
+        private enum Status {
+            SUCCESS("Success"),
+            FAILURE("Failure");
+            public String value;
+
+            Status(String value) {
+                this.value = value;
+            }
+        }
+
+        private enum CallType {
+            NORMAL_CALL("Normal Call"),
+            SOS_CALL("SOS Call");
+            public String value;
+
+            CallType(String value) {
+                this.value = value;
+            }
+        }
+
+        public CallAnalytics() {}
+
+        /**
+         * Collects and processes data related to calls once the call is terminated.
+         *
+         * @param isEmergency : Stores whether the call is an SOS call or not
+         * @param isOverIms : Stores whether the call is over IMS.
+         * @param rat : Stores the Radio Access Technology being used when the call ended
+         * @param simSlotIndex : Sim Slot from which call was operating.
+         * @param disconnectCause : Reason for call disconnect.
+         */
+        public void onCallTerminated(
+                boolean isEmergency,
+                boolean isOverIms,
+                int rat,
+                int simSlotIndex,
+                int disconnectCause) {
+            String disconnectCauseString;
+            String status;
+            String callType;
+            if (isEmergency) {
+                callType = CallType.SOS_CALL.value;
+            } else {
+                callType = CallType.NORMAL_CALL.value;
+            }
+            if (isOverIms) {
+                disconnectCauseString = sImsCodeMap.get(disconnectCause);
+                status =
+                        disconnectCause == ImsReasonInfo.CODE_USER_TERMINATED
+                                || disconnectCause
+                                == ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE
+                                ? Status.SUCCESS.value
+                                : Status.FAILURE.value;
+            } else {
+                disconnectCauseString = DisconnectCause.toString(disconnectCause);
+                status =
+                        disconnectCause == DisconnectCause.LOCAL
+                                || disconnectCause == DisconnectCause.NORMAL
+                                ? Status.SUCCESS.value
+                                : Status.FAILURE.value;
+            }
+            String ratString = TelephonyManager.getNetworkTypeName(rat);
+            sendDataToProvider(callType, status, simSlotIndex, rat, ratString,
+                    disconnectCause, disconnectCauseString);
+        }
+
+        private void sendDataToProvider(String callType, String status, int simSlotIndex,
+                int rat, String ratString, int disconnectCause, String disconnectCauseString) {
+            mExecutorService.execute(() -> {
+                mCallAnalyticsProvider.insertDataToDb(
+                        callType, status, simSlotIndex, ratString, disconnectCauseString);
+                ArrayList<String> data;
+                data =
+                        new ArrayList<>(
+                                List.of(
+                                        callType,
+                                        status,
+                                        disconnectCauseString,
+                                        "(" + disconnectCause + ")",
+                                        ratString,
+                                        "(" + rat + ")",
+                                        Integer.toString(simSlotIndex)));
+                Rlog.d(TAG, data.toString());
+            });
+        }
+
+        private static final Map<Integer, String> sImsCodeMap;
+
+        static {
+            sImsCodeMap = new HashMap<>();
+            sImsCodeMap.put(ImsReasonInfo.CODE_UNSPECIFIED, "CODE_UNSPECIFIED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT, "CODE_LOCAL_ILLEGAL_ARGUMENT");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE, "CODE_LOCAL_ILLEGAL_STATE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR, "CODE_LOCAL_INTERNAL_ERROR");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN, "CODE_LOCAL_IMS_SERVICE_DOWN");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL, "CODE_LOCAL_NO_PENDING_CALL");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
+                    "CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_POWER_OFF, "CODE_LOCAL_POWER_OFF");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, "CODE_LOCAL_LOW_BATTERY");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE, "CODE_LOCAL_NETWORK_NO_SERVICE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE,
+                    "CODE_LOCAL_NETWORK_NO_LTE_COVERAGE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING, "CODE_LOCAL_NETWORK_ROAMING");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED, "CODE_LOCAL_NETWORK_IP_CHANGED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE, "CODE_LOCAL_SERVICE_UNAVAILABLE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, "CODE_LOCAL_NOT_REGISTERED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_CALL_EXCEEDED, "CODE_LOCAL_CALL_EXCEEDED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_CALL_BUSY, "CODE_LOCAL_CALL_BUSY");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_CALL_DECLINE, "CODE_LOCAL_CALL_DECLINE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING,
+                    "CODE_LOCAL_CALL_VCC_ON_PROGRESSING");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED,
+                    "CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+                    "CODE_LOCAL_CALL_CS_RETRY_REQUIRED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED,
+                    "CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED, "CODE_LOCAL_CALL_TERMINATED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE, "CODE_LOCAL_HO_NOT_FEASIBLE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING, "CODE_TIMEOUT_1XX_WAITING");
+            sImsCodeMap.put(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER, "CODE_TIMEOUT_NO_ANSWER");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE,
+                    "CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_CALL_BARRED, "CODE_CALL_BARRED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_FDN_BLOCKED, "CODE_FDN_BLOCKED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_IMEI_NOT_ACCEPTED, "CODE_IMEI_NOT_ACCEPTED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_DIAL_MODIFIED_TO_USSD, "CODE_DIAL_MODIFIED_TO_USSD");
+            sImsCodeMap.put(ImsReasonInfo.CODE_DIAL_MODIFIED_TO_SS, "CODE_DIAL_MODIFIED_TO_SS");
+            sImsCodeMap.put(ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL, "CODE_DIAL_MODIFIED_TO_DIAL");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL_VIDEO,
+                    "CODE_DIAL_MODIFIED_TO_DIAL_VIDEO");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL,
+                    "CODE_DIAL_VIDEO_MODIFIED_TO_DIAL");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO,
+                    "CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_SS, "CODE_DIAL_VIDEO_MODIFIED_TO_SS");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_USSD,
+                    "CODE_DIAL_VIDEO_MODIFIED_TO_USSD");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_REDIRECTED, "CODE_SIP_REDIRECTED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_BAD_REQUEST, "CODE_SIP_BAD_REQUEST");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_FORBIDDEN, "CODE_SIP_FORBIDDEN");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_NOT_FOUND, "CODE_SIP_NOT_FOUND");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_NOT_SUPPORTED, "CODE_SIP_NOT_SUPPORTED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT, "CODE_SIP_REQUEST_TIMEOUT");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE,
+                    "CODE_SIP_TEMPRARILY_UNAVAILABLE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_BAD_ADDRESS, "CODE_SIP_BAD_ADDRESS");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_BUSY, "CODE_SIP_BUSY");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED, "CODE_SIP_REQUEST_CANCELLED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE, "CODE_SIP_NOT_ACCEPTABLE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_NOT_REACHABLE, "CODE_SIP_NOT_REACHABLE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_CLIENT_ERROR, "CODE_SIP_CLIENT_ERROR");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_TRANSACTION_DOES_NOT_EXIST,
+                    "CODE_SIP_TRANSACTION_DOES_NOT_EXIST");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_SERVER_INTERNAL_ERROR, "CODE_SIP_SERVER_INTERNAL_ERROR");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE, "CODE_SIP_SERVICE_UNAVAILABLE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_SERVER_TIMEOUT, "CODE_SIP_SERVER_TIMEOUT");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_SERVER_ERROR, "CODE_SIP_SERVER_ERROR");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_USER_REJECTED, "CODE_SIP_USER_REJECTED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_GLOBAL_ERROR, "CODE_SIP_GLOBAL_ERROR");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE, "CODE_EMERGENCY_TEMP_FAILURE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE, "CODE_EMERGENCY_PERM_FAILURE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_USER_MARKED_UNWANTED, "CODE_SIP_USER_MARKED_UNWANTED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_METHOD_NOT_ALLOWED, "CODE_SIP_METHOD_NOT_ALLOWED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_PROXY_AUTHENTICATION_REQUIRED,
+                    "CODE_SIP_PROXY_AUTHENTICATION_REQUIRED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_REQUEST_ENTITY_TOO_LARGE,
+                    "CODE_SIP_REQUEST_ENTITY_TOO_LARGE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_REQUEST_URI_TOO_LARGE, "CODE_SIP_REQUEST_URI_TOO_LARGE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_EXTENSION_REQUIRED, "CODE_SIP_EXTENSION_REQUIRED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_INTERVAL_TOO_BRIEF, "CODE_SIP_INTERVAL_TOO_BRIEF");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST,
+                    "CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_LOOP_DETECTED, "CODE_SIP_LOOP_DETECTED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_TOO_MANY_HOPS, "CODE_SIP_TOO_MANY_HOPS");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_AMBIGUOUS, "CODE_SIP_AMBIGUOUS");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_REQUEST_PENDING, "CODE_SIP_REQUEST_PENDING");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SIP_UNDECIPHERABLE, "CODE_SIP_UNDECIPHERABLE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_MEDIA_INIT_FAILED, "CODE_MEDIA_INIT_FAILED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_MEDIA_NO_DATA, "CODE_MEDIA_NO_DATA");
+            sImsCodeMap.put(ImsReasonInfo.CODE_MEDIA_NOT_ACCEPTABLE, "CODE_MEDIA_NOT_ACCEPTABLE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_MEDIA_UNSPECIFIED, "CODE_MEDIA_UNSPECIFIED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_USER_TERMINATED, "CODE_USER_TERMINATED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_USER_NOANSWER, "CODE_USER_NOANSWER");
+            sImsCodeMap.put(ImsReasonInfo.CODE_USER_IGNORE, "CODE_USER_IGNORE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_USER_DECLINE, "CODE_USER_DECLINE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_LOW_BATTERY, "CODE_LOW_BATTERY");
+            sImsCodeMap.put(ImsReasonInfo.CODE_BLACKLISTED_CALL_ID, "CODE_BLACKLISTED_CALL_ID");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, "CODE_USER_TERMINATED_BY_REMOTE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_USER_REJECTED_SESSION_MODIFICATION,
+                    "CODE_USER_REJECTED_SESSION_MODIFICATION");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_USER_CANCELLED_SESSION_MODIFICATION,
+                    "CODE_USER_CANCELLED_SESSION_MODIFICATION");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SESSION_MODIFICATION_FAILED,
+                    "CODE_SESSION_MODIFICATION_FAILED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_UT_NOT_SUPPORTED, "CODE_UT_NOT_SUPPORTED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE, "CODE_UT_SERVICE_UNAVAILABLE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_UT_OPERATION_NOT_ALLOWED, "CODE_UT_OPERATION_NOT_ALLOWED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_UT_NETWORK_ERROR, "CODE_UT_NETWORK_ERROR");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH, "CODE_UT_CB_PASSWORD_MISMATCH");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL, "CODE_UT_SS_MODIFIED_TO_DIAL");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD, "CODE_UT_SS_MODIFIED_TO_USSD");
+            sImsCodeMap.put(ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS, "CODE_UT_SS_MODIFIED_TO_SS");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO,
+                    "CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO");
+            sImsCodeMap.put(ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED, "CODE_ECBM_NOT_SUPPORTED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED,
+                    "CODE_MULTIENDPOINT_NOT_SUPPORTED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_REGISTRATION_ERROR, "CODE_REGISTRATION_ERROR");
+            sImsCodeMap.put(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE, "CODE_ANSWERED_ELSEWHERE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC, "CODE_CALL_PULL_OUT_OF_SYNC");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL, "CODE_CALL_END_CAUSE_CALL_PULL");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE,
+                    "CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_REJECTED_ELSEWHERE, "CODE_REJECTED_ELSEWHERE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SUPP_SVC_FAILED, "CODE_SUPP_SVC_FAILED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_SUPP_SVC_CANCELLED, "CODE_SUPP_SVC_CANCELLED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SUPP_SVC_REINVITE_COLLISION,
+                    "CODE_SUPP_SVC_REINVITE_COLLISION");
+            sImsCodeMap.put(ImsReasonInfo.CODE_IWLAN_DPD_FAILURE, "CODE_IWLAN_DPD_FAILURE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_EPDG_TUNNEL_ESTABLISH_FAILURE,
+                    "CODE_EPDG_TUNNEL_ESTABLISH_FAILURE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_EPDG_TUNNEL_REKEY_FAILURE, "CODE_EPDG_TUNNEL_REKEY_FAILURE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_EPDG_TUNNEL_LOST_CONNECTION,
+                    "CODE_EPDG_TUNNEL_LOST_CONNECTION");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED,
+                    "CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_REMOTE_CALL_DECLINE, "CODE_REMOTE_CALL_DECLINE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_DATA_LIMIT_REACHED, "CODE_DATA_LIMIT_REACHED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_DATA_DISABLED, "CODE_DATA_DISABLED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_WIFI_LOST, "CODE_WIFI_LOST");
+            sImsCodeMap.put(ImsReasonInfo.CODE_IKEV2_AUTH_FAILURE, "CODE_IKEV2_AUTH_FAILURE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_OFF, "CODE_RADIO_OFF");
+            sImsCodeMap.put(ImsReasonInfo.CODE_NO_VALID_SIM, "CODE_NO_VALID_SIM");
+            sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_INTERNAL_ERROR, "CODE_RADIO_INTERNAL_ERROR");
+            sImsCodeMap.put(ImsReasonInfo.CODE_NETWORK_RESP_TIMEOUT, "CODE_NETWORK_RESP_TIMEOUT");
+            sImsCodeMap.put(ImsReasonInfo.CODE_NETWORK_REJECT, "CODE_NETWORK_REJECT");
+            sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_ACCESS_FAILURE, "CODE_RADIO_ACCESS_FAILURE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_LINK_FAILURE, "CODE_RADIO_LINK_FAILURE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_LINK_LOST, "CODE_RADIO_LINK_LOST");
+            sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_UPLINK_FAILURE, "CODE_RADIO_UPLINK_FAILURE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_SETUP_FAILURE, "CODE_RADIO_SETUP_FAILURE");
+            sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_RELEASE_NORMAL, "CODE_RADIO_RELEASE_NORMAL");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_RADIO_RELEASE_ABNORMAL, "CODE_RADIO_RELEASE_ABNORMAL");
+            sImsCodeMap.put(ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED, "CODE_ACCESS_CLASS_BLOCKED");
+            sImsCodeMap.put(ImsReasonInfo.CODE_NETWORK_DETACH, "CODE_NETWORK_DETACH");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL,
+                    "CODE_SIP_ALTERNATE_EMERGENCY_CALL");
+            sImsCodeMap.put(ImsReasonInfo.CODE_UNOBTAINABLE_NUMBER, "CODE_UNOBTAINABLE_NUMBER");
+            sImsCodeMap.put(ImsReasonInfo.CODE_NO_CSFB_IN_CS_ROAM, "CODE_NO_CSFB_IN_CS_ROAM");
+            sImsCodeMap.put(ImsReasonInfo.CODE_REJECT_UNKNOWN, "CODE_REJECT_UNKNOWN");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_ONGOING_CALL_WAITING_DISABLED,
+                    "CODE_REJECT_ONGOING_CALL_WAITING_DISABLED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_CALL_ON_OTHER_SUB, "CODE_REJECT_CALL_ON_OTHER_SUB");
+            sImsCodeMap.put(ImsReasonInfo.CODE_REJECT_1X_COLLISION, "CODE_REJECT_1X_COLLISION");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_SERVICE_NOT_REGISTERED,
+                    "CODE_REJECT_SERVICE_NOT_REGISTERED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_CALL_TYPE_NOT_ALLOWED,
+                    "CODE_REJECT_CALL_TYPE_NOT_ALLOWED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_ONGOING_E911_CALL, "CODE_REJECT_ONGOING_E911_CALL");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_ONGOING_CALL_SETUP, "CODE_REJECT_ONGOING_CALL_SETUP");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_MAX_CALL_LIMIT_REACHED,
+                    "CODE_REJECT_MAX_CALL_LIMIT_REACHED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_UNSUPPORTED_SIP_HEADERS,
+                    "CODE_REJECT_UNSUPPORTED_SIP_HEADERS");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_UNSUPPORTED_SDP_HEADERS,
+                    "CODE_REJECT_UNSUPPORTED_SDP_HEADERS");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_ONGOING_CALL_TRANSFER,
+                    "CODE_REJECT_ONGOING_CALL_TRANSFER");
+            sImsCodeMap.put(ImsReasonInfo.CODE_REJECT_INTERNAL_ERROR, "CODE_REJECT_INTERNAL_ERROR");
+            sImsCodeMap.put(ImsReasonInfo.CODE_REJECT_QOS_FAILURE, "CODE_REJECT_QOS_FAILURE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_ONGOING_HANDOVER, "CODE_REJECT_ONGOING_HANDOVER");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_VT_TTY_NOT_ALLOWED, "CODE_REJECT_VT_TTY_NOT_ALLOWED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_ONGOING_CALL_UPGRADE,
+                    "CODE_REJECT_ONGOING_CALL_UPGRADE");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED,
+                    "CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_ONGOING_CONFERENCE_CALL,
+                    "CODE_REJECT_ONGOING_CONFERENCE_CALL");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_VT_AVPF_NOT_ALLOWED,
+                    "CODE_REJECT_VT_AVPF_NOT_ALLOWED");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_ONGOING_ENCRYPTED_CALL,
+                    "CODE_REJECT_ONGOING_ENCRYPTED_CALL");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_REJECT_ONGOING_CS_CALL, "CODE_REJECT_ONGOING_CS_CALL");
+            sImsCodeMap.put(ImsReasonInfo.CODE_NETWORK_CONGESTION, "CODE_NETWORK_CONGESTION");
+            sImsCodeMap.put(
+                    ImsReasonInfo.CODE_RETRY_ON_IMS_WITHOUT_RTT, "CODE_RETRY_ON_IMS_WITHOUT_RTT");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_1, "CODE_OEM_CAUSE_1");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_2, "CODE_OEM_CAUSE_2");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_3, "CODE_OEM_CAUSE_3");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_4, "CODE_OEM_CAUSE_4");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_5, "CODE_OEM_CAUSE_5");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_6, "CODE_OEM_CAUSE_6");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_7, "CODE_OEM_CAUSE_7");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_8, "CODE_OEM_CAUSE_8");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_9, "CODE_OEM_CAUSE_9");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_10, "CODE_OEM_CAUSE_10");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_11, "CODE_OEM_CAUSE_11");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_12, "CODE_OEM_CAUSE_12");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_13, "CODE_OEM_CAUSE_13");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_14, "CODE_OEM_CAUSE_14");
+            sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_15, "CODE_OEM_CAUSE_15");
+        }
+    }
+
+    /**
+     * Implements and Registers the required Listeners and BroadcastReceivers for receiving
+     * ServiceState related information. Performs required logic on received data and then Passes
+     * the information to its provider class for further processing.
+     */
+    public class ServiceStateAnalytics extends TelephonyCallback
+            implements TelephonyCallback.ServiceStateListener {
+        private final Executor mExecutor;
+        private static final String TAG = ServiceStateAnalytics.class.getSimpleName();
+        private static final int BUFFER_TIME = 10000;
+
+        private TelephonyManager mTelephonyManager;
+
+        private enum DeviceStatus {
+            APM,
+            CELLULAR_OOS_WITH_IWLAN,
+            NO_NETWORK_COVERAGE,
+            SIM_ABSENT,
+            IN_SERVICE;
+        }
+
+        private final AtomicReference<TimeStampedServiceState> mLastState =
+                new AtomicReference<>(null);
+        private static final String NA = "NA";
+        private final BroadcastReceiver mBroadcastReceiver =
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        final long now = getTimeMillis();
+                        if (intent.getAction()
+                                .equals(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)) {
+                            int simState =
+                                    intent.getIntExtra(
+                                            TelephonyManager.EXTRA_SIM_STATE,
+                                            TelephonyManager.SIM_STATE_UNKNOWN);
+                            if (simState == TelephonyManager.SIM_STATE_ABSENT) {
+                                Rlog.d("AnkitSimAbsent", "Sim is Absent");
+                                logSimAbsentState();
+                            }
+                        }
+                    }
+                };
+
+        protected ServiceStateAnalytics(Executor executor) {
+            super();
+            mExecutor = executor;
+            IntentFilter mIntentFilter =
+                    new IntentFilter(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
+            mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
+        }
+
+        @Override
+        public void onServiceStateChanged(@NonNull ServiceState serviceState) {
+            int dataRegState = serviceState.getDataRegState();
+            int voiceRegState = serviceState.getVoiceRegState();
+            int voiceRadioTechnology = serviceState.getRilVoiceRadioTechnology();
+            int dataRadioTechnology = serviceState.getRilDataRadioTechnology();
+
+            mExecutorService.execute(() -> {
+                logServiceState(dataRegState, voiceRegState, voiceRadioTechnology,
+                        dataRadioTechnology);
+            });
+        }
+
+        private void logServiceState(
+                int dataRegState,
+                int voiceRegState,
+                int voiceRadioTechnology,
+                int dataRadioTechnology) {
+            long now = getTimeMillis();
+            String voiceRadioTechnologyName =
+                    ServiceState.rilRadioTechnologyToString(voiceRadioTechnology);
+            String dataRadioTechnologyName =
+                    ServiceState.rilRadioTechnologyToString(dataRadioTechnology);
+
+            if (isAirplaneModeOn()) {
+                if (dataRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        && dataRegState == ServiceState.STATE_IN_SERVICE) {
+                    logOosWithIwlan(now);
+                } else {
+                    logAirplaneModeServiceState(now);
+                }
+            } else {
+                if (voiceRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
+                        && dataRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+                    logNoNetworkCoverage(now);
+
+                } else if (voiceRadioTechnology != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
+                        && dataRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+                    if (voiceRegState == ServiceState.STATE_IN_SERVICE) {
+                        logInServiceData(voiceRadioTechnologyName, now);
+                    } else {
+                        logNoNetworkCoverage(now);
+                    }
+                } else if (voiceRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+                    if (dataRegState == ServiceState.STATE_IN_SERVICE) {
+                        if (dataRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
+                            logOosWithIwlan(now);
+                        } else {
+                            logInServiceData(dataRadioTechnologyName, now);
+                        }
+                    } else {
+                        logNoNetworkCoverage(now);
+                    }
+                } else {
+                    if (dataRegState == ServiceState.STATE_IN_SERVICE
+                            || voiceRegState == ServiceState.STATE_IN_SERVICE) {
+                        logInServiceData(voiceRadioTechnologyName, now);
+                    } else {
+                        logNoNetworkCoverage(now);
+                    }
+                }
+            }
+        }
+
+        private void logSimAbsentState() {
+            long now = getTimeMillis();
+            TimeStampedServiceState currentState =
+                    new TimeStampedServiceState(
+                            mSlotIndex, NA, DeviceStatus.SIM_ABSENT.name(), now);
+            setCurrentStateAndAddLastState(currentState, now);
+        }
+        private void logOosWithIwlan(long now) {
+            TimeStampedServiceState currentState =
+                    new TimeStampedServiceState(mSlotIndex, NA,
+                            DeviceStatus.CELLULAR_OOS_WITH_IWLAN.name(), now);
+            setCurrentStateAndAddLastState(currentState, now);
+        }
+
+        private void logAirplaneModeServiceState(long now) {
+            TimeStampedServiceState currentState =
+                    new TimeStampedServiceState(mSlotIndex, NA, DeviceStatus.APM.name(), now);
+            setCurrentStateAndAddLastState(currentState, now);
+        }
+
+        private void logNoNetworkCoverage(long now) {
+            TimeStampedServiceState currentState =
+                    new TimeStampedServiceState(
+                            mSlotIndex, NA, DeviceStatus.NO_NETWORK_COVERAGE.name(), now);
+            setCurrentStateAndAddLastState(currentState, now);
+        }
+
+        private void logInServiceData(String rat, long now) {
+            TimeStampedServiceState currentState =
+                    new TimeStampedServiceState(
+                            mSlotIndex, rat, DeviceStatus.IN_SERVICE.name(), now);
+            setCurrentStateAndAddLastState(currentState, now);
+        }
+
+        private void setCurrentStateAndAddLastState(
+                TimeStampedServiceState currentState, long now) {
+            TimeStampedServiceState lastState = mLastState.getAndSet(currentState);
+            addData(lastState, now);
+        }
+
+        private void addData(TimeStampedServiceState lastState, long now) {
+            if (lastState == null) {
+                return;
+            }
+            if (now - lastState.mTimestampStart < BUFFER_TIME) {
+                return;
+            }
+            Rlog.d(TAG, "Last State = " + lastState.toString() + "End = " + now);
+            mServiceStateAnalyticsProvider.insertDataToDb(lastState, now);
+        }
+
+        private void recordCurrentStateBeforeDump() {
+            long now = getTimeMillis();
+            Rlog.d("RecordingStateBDump", "Recording " + now);
+            TimeStampedServiceState currentState = mLastState.get();
+            mLastState.set(createCopyWithUpdatedTimestamp(currentState));
+            addData(currentState, now);
+        }
+
+        private TimeStampedServiceState createCopyWithUpdatedTimestamp(
+                TimeStampedServiceState currentState) {
+            if (currentState == null) {
+                return null;
+            }
+            long now = getTimeMillis();
+            TimeStampedServiceState state =
+                    new TimeStampedServiceState(
+                            currentState.mSlotIndex,
+                            currentState.mRAT,
+                            currentState.mDeviceStatus,
+                            now);
+            return state;
+        }
+
+        private boolean isAirplaneModeOn() {
+            return Settings.Global.getInt(
+                    mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0)
+                    != 0;
+        }
+
+        protected long getTimeMillis() {
+            return SystemClock.elapsedRealtime();
+        }
+
+        void registerMyListener(Context context, int subId) {
+            try {
+                mTelephonyManager =
+                        context.getSystemService(TelephonyManager.class)
+                                .createForSubscriptionId(subId);
+                mTelephonyManager.registerTelephonyCallback(mExecutor, this);
+
+            } catch (NullPointerException e) {
+                log("Null pointer exception caught " + e);
+            }
+        }
+
+        void unregisterMyListener(int subId) {
+            mTelephonyManager.unregisterTelephonyCallback(this);
+        }
+
+        private void log(String s) {
+            Rlog.d(ServiceStateAnalytics.class.getSimpleName(), s);
+        }
+
+        /**
+         * Serves the functionality of storing service state related information,
+         * Along with the timestamp at which the state was detected.
+         */
+        public static class TimeStampedServiceState {
+            protected final int mSlotIndex;
+            protected final String mRAT;
+            protected final String mDeviceStatus;
+            protected final long mTimestampStart;
+
+            public TimeStampedServiceState(
+                    int slotIndex, String rat, String deviceStatus, long timestampStart) {
+                mSlotIndex = slotIndex;
+                mRAT = rat;
+                mDeviceStatus = deviceStatus;
+                mTimestampStart = timestampStart;
+            }
+
+            @Override
+            public String toString() {
+                return "SlotIndex = "
+                        + mSlotIndex
+                        + " RAT = "
+                        + mRAT
+                        + " DeviceStatus = "
+                        + mDeviceStatus
+                        + "TimeStampStart = "
+                        + mTimestampStart;
+            }
+            /** Getter function for slotIndex */
+            public int getSlotIndex() {
+                return mSlotIndex;
+            }
+
+            /** Getter function for state start Timestamp */
+            public long getTimestampStart() {
+                return mTimestampStart;
+            }
+
+            /** Getter function for device Status */
+            public String getDeviceStatus() {
+                return mDeviceStatus;
+            }
+
+            /** Getter function for radio access technology  */
+            public String getRAT() {
+                return mRAT;
+            }
+        }
+    }
+
+    /**
+     * Provides implementation for processing received Sms related data. Implements functions to
+     * handle various scenarios pertaining to Sms. Passes the data to its provider for further
+     * processing.
+     */
+    public class SmsMmsAnalytics {
+        private static final String TAG = SmsMmsAnalytics.class.getSimpleName();
+        public SmsMmsAnalytics() {
+
+        }
+
+        /** Collects Outgoing Sms related information. */
+        public void onOutgoingSms(boolean isOverIms, @SmsManager.Result int sendErrorCode) {
+            Rlog.d(
+                    TAG,
+                    "Is Over Ims = "
+                            + isOverIms
+                            + " sendErrorCode = "
+                            + sendErrorCode
+                            + "SlotInfo ="
+                            + mSlotIndex);
+            logOutgoingSms(isOverIms, sendErrorCode);
+        }
+
+        /** Collects Successful Incoming Sms related information. */
+        public void onIncomingSmsSuccess(@InboundSmsHandler.SmsSource int smsSource) {
+            Rlog.d(TAG, " smsSource = " + smsSource);
+            String status = "Success";
+            String failureReason = "NA";
+            logIncomingSms(smsSource, status, failureReason);
+        }
+
+        /** Collects Failed Incoming Multipart Sms related information. */
+        public void onDroppedIncomingMultipartSms() {
+            String status = "Failure";
+            String type = "SMS Incoming";
+            // Mark the RAT as unknown since it might have changed over time.
+            int rat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+            String ratString = ServiceState.rilRadioTechnologyToString(rat);
+            String failureReason = "INCOMING_SMS__ERROR__SMS_ERROR_GENERIC";
+            sendDataToProvider(status, type, ratString, failureReason);
+        }
+
+        /** Collects Failed Incoming Sms related information. */
+        public void onIncomingSmsError(@InboundSmsHandler.SmsSource int smsSource, int result) {
+            String status = "Failure";
+            String failureReason = getIncomingSmsErrorString(result);
+            logIncomingSms(smsSource, status, failureReason);
+            Rlog.d(
+                    TAG,
+                    " smsSource = "
+                            + smsSource
+                            + "Result = "
+                            + result
+                            + "IncomingError = "
+                            + failureReason
+                            + "("
+                            + getIncomingError(result)
+                            + ")");
+        }
+
+        private void logOutgoingSms(boolean isOverIms, @SmsManager.Result int sendErrorCode) {
+            try {
+                String type = "SMS Outgoing";
+                String status = sendErrorCode == 0 ? "Success" : "Failure";
+                int rat = getRat(isOverIms);
+                String ratString = TelephonyManager.getNetworkTypeName(rat);
+                String failureReason =
+                        status.equals("Success") ? "NA" : getSmsFailureReasonString(sendErrorCode);
+                Rlog.d(
+                        TAG,
+                        "SlotInfo = "
+                                + mSlotIndex
+                                + " Type = "
+                                + type
+                                + " Status = "
+                                + status
+                                + "RAT "
+                                + ratString
+                                + " "
+                                + rat
+                                + "Failure Reason = "
+                                + failureReason);
+                sendDataToProvider(status, type, ratString, failureReason);
+
+            } catch (Exception e) {
+                Rlog.d(TAG, "Error in SmsLogs" + e);
+            }
+        }
+
+        private void logIncomingSms(
+                @InboundSmsHandler.SmsSource int smsSource, String status, String failureReason) {
+            String type = "SMS Incoming";
+            try {
+                int rat = getRat(smsSource);
+                String ratString = TelephonyManager.getNetworkTypeName(rat);
+                sendDataToProvider(status, type, ratString, failureReason);
+                Rlog.d(
+                        TAG,
+                        "SlotInfo ="
+                                + mSlotIndex
+                                + " Type = "
+                                + type
+                                + " Status = "
+                                + status
+                                + " RAT "
+                                + ratString
+                                + " ("
+                                + rat
+                                + " ) Failure Reason = "
+                                + failureReason);
+            } catch (Exception e) {
+                Rlog.e(TAG, "Exception = " + e);
+            }
+        }
+
+        private void sendDataToProvider(
+                String status, String type, String rat, String failureReason) {
+            mExecutorService.execute(() -> {
+                mSmsMmsAnalyticsProvider.insertDataToDb(status, type, rat, failureReason);
+            });
+        }
+
+        private static int getIncomingError(int result) {
+            switch (result) {
+                case Activity.RESULT_OK:
+                case Intents.RESULT_SMS_HANDLED:
+                    return INCOMING_SMS__ERROR__SMS_SUCCESS;
+                case Intents.RESULT_SMS_OUT_OF_MEMORY:
+                    return INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY;
+                case Intents.RESULT_SMS_UNSUPPORTED:
+                    return INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED;
+                case Intents.RESULT_SMS_GENERIC_ERROR:
+                default:
+                    return INCOMING_SMS__ERROR__SMS_ERROR_GENERIC;
+            }
+        }
+
+        private static String getIncomingSmsErrorString(int result) {
+            switch (result) {
+                case Activity.RESULT_OK:
+                case Intents.RESULT_SMS_HANDLED:
+                    return "INCOMING_SMS__ERROR__SMS_SUCCESS";
+                case Intents.RESULT_SMS_OUT_OF_MEMORY:
+                    return "INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY";
+                case Intents.RESULT_SMS_UNSUPPORTED:
+                    return "INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED";
+                case Intents.RESULT_SMS_GENERIC_ERROR:
+                default:
+                    return "INCOMING_SMS__ERROR__SMS_ERROR_GENERIC";
+            }
+        }
+
+        @Nullable
+        private ServiceState getServiceState() {
+            Phone phone = mPhone;
+            if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+                phone = mPhone.getDefaultPhone();
+            }
+            ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
+            return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
+        }
+
+        @Annotation.NetworkType
+        private int getRat(@InboundSmsHandler.SmsSource int smsSource) {
+            if (smsSource == SOURCE_INJECTED_FROM_UNKNOWN) {
+                return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+            }
+            return getRat(smsSource == SOURCE_INJECTED_FROM_IMS);
+        }
+
+        @Annotation.NetworkType
+        private int getRat(boolean isOverIms) {
+            if (isOverIms) {
+                if (mPhone.getImsRegistrationTech()
+                        == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+                    return TelephonyManager.NETWORK_TYPE_IWLAN;
+                }
+            }
+            ServiceState serviceState = getServiceState();
+            return serviceState != null
+                    ? serviceState.getVoiceNetworkType()
+                    : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        }
+
+        private String getSmsFailureReasonString(int sendErrorCode) {
+            switch (sendErrorCode) {
+                case SmsManager.RESULT_ERROR_NONE:
+                    return "RESULT_ERROR_NONE";
+                case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
+                    return "RESULT_ERROR_GENERIC_FAILURE";
+                case SmsManager.RESULT_ERROR_RADIO_OFF:
+                    return "RESULT_ERROR_RADIO_OFF";
+                case SmsManager.RESULT_ERROR_NULL_PDU:
+                    return "RESULT_ERROR_NULL_PDU";
+                case SmsManager.RESULT_ERROR_NO_SERVICE:
+                    return "RESULT_ERROR_NO_SERVICE";
+                case SmsManager.RESULT_ERROR_LIMIT_EXCEEDED:
+                    return "RESULT_ERROR_LIMIT_EXCEEDED";
+                case SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE:
+                    return "RESULT_ERROR_FDN_CHECK_FAILURE";
+                case SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED:
+                    return "RESULT_ERROR_SHORT_CODE_NOT_ALLOWED";
+                case SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED:
+                    return "RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED";
+                case SmsManager.RESULT_RADIO_NOT_AVAILABLE:
+                    return "RESULT_RADIO_NOT_AVAILABLE";
+                case SmsManager.RESULT_NETWORK_REJECT:
+                    return "RESULT_NETWORK_REJECT";
+                case SmsManager.RESULT_INVALID_ARGUMENTS:
+                    return "RESULT_INVALID_ARGUMENTS";
+                case SmsManager.RESULT_INVALID_STATE:
+                    return "RESULT_INVALID_STATE";
+                case SmsManager.RESULT_NO_MEMORY:
+                    return "RESULT_NO_MEMORY";
+                case SmsManager.RESULT_INVALID_SMS_FORMAT:
+                    return "RESULT_INVALID_SMS_FORMAT";
+                case SmsManager.RESULT_SYSTEM_ERROR:
+                    return "RESULT_SYSTEM_ERROR";
+                case SmsManager.RESULT_MODEM_ERROR:
+                    return "RESULT_MODEM_ERROR";
+                case SmsManager.RESULT_NETWORK_ERROR:
+                    return "RESULT_NETWORK_ERROR";
+                case SmsManager.RESULT_INVALID_SMSC_ADDRESS:
+                    return "RESULT_INVALID_SMSC_ADDRESS";
+                case SmsManager.RESULT_OPERATION_NOT_ALLOWED:
+                    return "RESULT_OPERATION_NOT_ALLOWED";
+                case SmsManager.RESULT_INTERNAL_ERROR:
+                    return "RESULT_INTERNAL_ERROR";
+                case SmsManager.RESULT_NO_RESOURCES:
+                    return "RESULT_NO_RESOURCES";
+                case SmsManager.RESULT_CANCELLED:
+                    return "RESULT_CANCELLED";
+                case SmsManager.RESULT_REQUEST_NOT_SUPPORTED:
+                    return "RESULT_REQUEST_NOT_SUPPORTED";
+                case SmsManager.RESULT_NO_BLUETOOTH_SERVICE:
+                    return "RESULT_NO_BLUETOOTH_SERVICE";
+                case SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS:
+                    return "RESULT_INVALID_BLUETOOTH_ADDRESS";
+                case SmsManager.RESULT_BLUETOOTH_DISCONNECTED:
+                    return "RESULT_BLUETOOTH_DISCONNECTED";
+                case SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING:
+                    return "RESULT_UNEXPECTED_EVENT_STOP_SENDING";
+                case SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY:
+                    return "RESULT_SMS_BLOCKED_DURING_EMERGENCY";
+                case SmsManager.RESULT_SMS_SEND_RETRY_FAILED:
+                    return "RESULT_SMS_SEND_RETRY_FAILED";
+                case SmsManager.RESULT_REMOTE_EXCEPTION:
+                    return "RESULT_REMOTE_EXCEPTION";
+                case SmsManager.RESULT_NO_DEFAULT_SMS_APP:
+                    return "RESULT_NO_DEFAULT_SMS_APP";
+                case SmsManager.RESULT_USER_NOT_ALLOWED:
+                    return "RESULT_USER_NOT_ALLOWED";
+                case SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE:
+                    return "RESULT_RIL_RADIO_NOT_AVAILABLE";
+                case SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY:
+                    return "RESULT_RIL_SMS_SEND_FAIL_RETRY";
+                case SmsManager.RESULT_RIL_NETWORK_REJECT:
+                    return "RESULT_RIL_NETWORK_REJECT";
+                case SmsManager.RESULT_RIL_INVALID_STATE:
+                    return "RESULT_RIL_INVALID_STATE";
+                case SmsManager.RESULT_RIL_INVALID_ARGUMENTS:
+                    return "RESULT_RIL_INVALID_ARGUMENTS";
+                case SmsManager.RESULT_RIL_NO_MEMORY:
+                    return "RESULT_RIL_NO_MEMORY";
+                case SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED:
+                    return "RESULT_RIL_REQUEST_RATE_LIMITED";
+                case SmsManager.RESULT_RIL_INVALID_SMS_FORMAT:
+                    return "RESULT_RIL_INVALID_SMS_FORMAT";
+                case SmsManager.RESULT_RIL_SYSTEM_ERR:
+                    return "RESULT_RIL_SYSTEM_ERR";
+                case SmsManager.RESULT_RIL_ENCODING_ERR:
+                    return "RESULT_RIL_ENCODING_ERR";
+                case SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS:
+                    return "RESULT_RIL_INVALID_SMSC_ADDRESS";
+                case SmsManager.RESULT_RIL_MODEM_ERR:
+                    return "RESULT_RIL_MODEM_ERR";
+                case SmsManager.RESULT_RIL_NETWORK_ERR:
+                    return "RESULT_RIL_NETWORK_ERR";
+                case SmsManager.RESULT_RIL_INTERNAL_ERR:
+                    return "RESULT_RIL_INTERNAL_ERR";
+                case SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED:
+                    return "RESULT_RIL_REQUEST_NOT_SUPPORTED";
+                case SmsManager.RESULT_RIL_INVALID_MODEM_STATE:
+                    return "RESULT_RIL_INVALID_MODEM_STATE";
+                case SmsManager.RESULT_RIL_NETWORK_NOT_READY:
+                    return "RESULT_RIL_NETWORK_NOT_READY";
+                case SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED:
+                    return "RESULT_RIL_OPERATION_NOT_ALLOWED";
+                case SmsManager.RESULT_RIL_NO_RESOURCES:
+                    return "RESULT_RIL_NO_RESOURCES";
+                case SmsManager.RESULT_RIL_CANCELLED:
+                    return "RESULT_RIL_CANCELLED";
+                case SmsManager.RESULT_RIL_SIM_ABSENT:
+                    return "RESULT_RIL_SIM_ABSENT";
+                case SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED:
+                    return "RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED";
+                case SmsManager.RESULT_RIL_ACCESS_BARRED:
+                    return "RESULT_RIL_ACCESS_BARRED";
+                case SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL:
+                    return "RESULT_RIL_BLOCKED_DUE_TO_CALL";
+                case SmsManager.RESULT_RIL_GENERIC_ERROR:
+                    return "RESULT_RIL_GENERIC_ERROR";
+                case SmsManager.RESULT_RIL_INVALID_RESPONSE:
+                    return "RESULT_RIL_INVALID_RESPONSE";
+                case SmsManager.RESULT_RIL_SIM_PIN2:
+                    return "RESULT_RIL_SIM_PIN2";
+                case SmsManager.RESULT_RIL_SIM_PUK2:
+                    return "RESULT_RIL_SIM_PUK2";
+                case SmsManager.RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE:
+                    return "RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE";
+                case SmsManager.RESULT_RIL_SIM_ERROR:
+                    return "RESULT_RIL_SIM_ERROR";
+                case SmsManager.RESULT_RIL_INVALID_SIM_STATE:
+                    return "RESULT_RIL_INVALID_SIM_STATE";
+                case SmsManager.RESULT_RIL_NO_SMS_TO_ACK:
+                    return "RESULT_RIL_NO_SMS_TO_ACK";
+                case SmsManager.RESULT_RIL_SIM_BUSY:
+                    return "RESULT_RIL_SIM_BUSY";
+                case SmsManager.RESULT_RIL_SIM_FULL:
+                    return "RESULT_RIL_SIM_FULL";
+                case SmsManager.RESULT_RIL_NO_SUBSCRIPTION:
+                    return "RESULT_RIL_NO_SUBSCRIPTION";
+                case SmsManager.RESULT_RIL_NO_NETWORK_FOUND:
+                    return "RESULT_RIL_NO_NETWORK_FOUND";
+                case SmsManager.RESULT_RIL_DEVICE_IN_USE:
+                    return "RESULT_RIL_DEVICE_IN_USE";
+                case SmsManager.RESULT_RIL_ABORTED:
+                    return "RESULT_RIL_ABORTED";
+                default:
+                    return "NA";
+            }
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/analytics/TelephonyAnalyticsDatabase.java b/src/java/com/android/internal/telephony/analytics/TelephonyAnalyticsDatabase.java
new file mode 100644
index 0000000..673eef9
--- /dev/null
+++ b/src/java/com/android/internal/telephony/analytics/TelephonyAnalyticsDatabase.java
@@ -0,0 +1,79 @@
+/*
+ * 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.internal.telephony.analytics;
+
+import android.provider.BaseColumns;
+
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Defines the tables classes which are present in the Telephony Analytics Database, private
+ * constructor to prevent instantiation.
+ */
+public final class TelephonyAnalyticsDatabase {
+    private TelephonyAnalyticsDatabase() {}
+
+    public static final DateTimeFormatter DATE_FORMAT =
+            DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault());
+
+    /**
+     * CallAnalyticsTable class defines the columns in the CallTable Implements the BaseColumns
+     * class.
+     */
+    public static final class CallAnalyticsTable implements BaseColumns {
+        public static final String TABLE_NAME = "CallDataLogs";
+        public static final String LOG_DATE = "LogDate";
+        public static final String CALL_STATUS = "CallStatus";
+        public static final String CALL_TYPE = "CallType";
+        public static final String RAT = "RAT";
+        public static final String SLOT_ID = "SlotID";
+        public static final String FAILURE_REASON = "FailureReason";
+        public static final String RELEASE_VERSION = "ReleaseVersion";
+        public static final String COUNT = "Count";
+    }
+
+    /**
+     * SmsMmsAnalyticsTable class defines the columns in the SmsMmsTable Implements the BaseColumns
+     * class.
+     */
+    public static final class SmsMmsAnalyticsTable implements BaseColumns {
+        public static final String TABLE_NAME = "SmsMmsDataLogs";
+        public static final String LOG_DATE = "LogDate";
+        public static final String SMS_MMS_STATUS = "SmsMmsStatus";
+        public static final String SMS_MMS_TYPE = "SmsMmsType";
+        public static final String SLOT_ID = "SlotID";
+        public static final String RAT = "RAT";
+        public static final String FAILURE_REASON = "FailureReason";
+        public static final String RELEASE_VERSION = "ReleaseVersion";
+        public static final String COUNT = "Count";
+    }
+
+    /**
+     * ServiceStateAnalyticsTable class defines the columns in the ServiceStateTable Implements the
+     * BaseColumns class.
+     */
+    public static final class ServiceStateAnalyticsTable implements BaseColumns {
+        public static final String TABLE_NAME = "ServiceStateLogs";
+        public static final String LOG_DATE = "LogDate";
+        public static final String TIME_DURATION = "TimeDuration";
+        public static final String SLOT_ID = "SlotID";
+        public static final String RAT = "RAT";
+        public static final String DEVICE_STATUS = "DeviceStatus";
+        public static final String RELEASE_VERSION = "ReleaseVersion";
+    }
+}
diff --git a/src/java/com/android/internal/telephony/analytics/TelephonyAnalyticsProvider.java b/src/java/com/android/internal/telephony/analytics/TelephonyAnalyticsProvider.java
new file mode 100644
index 0000000..32443cd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/analytics/TelephonyAnalyticsProvider.java
@@ -0,0 +1,30 @@
+/*
+ * 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.internal.telephony.analytics;
+
+import java.util.ArrayList;
+/**
+ * Interface for Telephony Provider Classes
+ */
+public interface TelephonyAnalyticsProvider {
+    /**
+     * Aggregates all information from the db.
+     * Used when the bugreport is to be pulled.
+     * @return All the aggregated information in a ArrayList.
+     */
+    ArrayList<String> aggregate();
+}
diff --git a/src/java/com/android/internal/telephony/analytics/TelephonyAnalyticsUtil.java b/src/java/com/android/internal/telephony/analytics/TelephonyAnalyticsUtil.java
new file mode 100644
index 0000000..78b607b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/analytics/TelephonyAnalyticsUtil.java
@@ -0,0 +1,187 @@
+/*
+ * 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.internal.telephony.analytics;
+
+import static com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.DATE_FORMAT;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.telephony.Rlog;
+
+import java.util.Calendar;
+
+/**
+ * Singleton Utility class to support TelephonyAnalytics Extends SQLiteOpenHelper class. Supports db
+ * related operations which includes creating tables,insertion,updation,deletion. Supports some
+ * generic functionality like getting the Cursor resulting from a query.
+ */
+public class TelephonyAnalyticsUtil extends SQLiteOpenHelper {
+    private static TelephonyAnalyticsUtil sTelephonyAnalyticsUtil;
+    private static final String DATABASE_NAME = "telephony_analytics.db";
+    private static final int DATABASE_VERSION = 10;
+    private static final String TAG = TelephonyAnalyticsUtil.class.getSimpleName();
+    private static final int MAX_ENTRIES_LIMIT = 1000;
+    private static final int CUTOFF_MONTHS = 2;
+
+    private TelephonyAnalyticsUtil(Context context) {
+        super(context, DATABASE_NAME, null, DATABASE_VERSION);
+    }
+
+    /**
+     * Get the instance of the TelephonyAnalyticsUtil class. Instantiates the TelephonyAnalyticsUtil
+     * object sTelephonyAnalyticsUtil only once.
+     *
+     * @return Returns the TelephonyAnalyticsUtil object sTelephonyAnalyticsUtil.
+     */
+    public static synchronized TelephonyAnalyticsUtil getInstance(Context context) {
+        if (sTelephonyAnalyticsUtil == null) {
+            sTelephonyAnalyticsUtil = new TelephonyAnalyticsUtil(context);
+        }
+        return sTelephonyAnalyticsUtil;
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {}
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
+
+    /**
+     * Uses Util class functionality to create CallTable in db
+     *
+     * @param createTableQuery : CallTable Schema
+     */
+    @VisibleForTesting
+    public synchronized void createTable(String createTableQuery) {
+        try {
+            SQLiteDatabase db = getWritableDatabase();
+            db.execSQL(createTableQuery);
+        } catch (Exception e) {
+            Rlog.e(TAG, "Error during table creation : " + e);
+        }
+    }
+
+    /** Utility function that performs insertion on the given database table */
+    @VisibleForTesting
+    public synchronized void insert(String tableName, ContentValues values) {
+        try {
+            SQLiteDatabase db = getWritableDatabase();
+            db.insert(tableName, null, values);
+        } catch (SQLException e) {
+            Rlog.e(TAG, "error occurred during insertion");
+        }
+    }
+
+    /** Utility function that performs update query on the given database table */
+    @VisibleForTesting
+    public synchronized int update(
+            String table, ContentValues values, String whereClause, String[] whereArgs) {
+        int rowsAffected = -1;
+        try {
+            SQLiteDatabase db = getWritableDatabase();
+            rowsAffected = db.update(table, values, whereClause, whereArgs);
+
+        } catch (SQLException e) {
+            Rlog.e(TAG, "Error during update.");
+        }
+        return rowsAffected;
+    }
+
+    /**
+     * @Return the cursor object obtained from running a query based on given parameters.
+     */
+    @VisibleForTesting
+    public synchronized Cursor getCursor(
+            String tableName,
+            String[] columns,
+            String selection,
+            String[] selectionArgs,
+            String groupBy,
+            String having,
+            String orderBy,
+            String limit) {
+
+        Cursor cursor = null;
+        try {
+            SQLiteDatabase db = getReadableDatabase();
+            cursor =
+                    db.query(
+                            tableName,
+                            columns,
+                            selection,
+                            selectionArgs,
+                            groupBy,
+                            having,
+                            orderBy,
+                            limit);
+        } catch (SQLException e) {
+            Rlog.e(TAG, "Error during querying for getCursor()" + e);
+        }
+        return cursor;
+    }
+
+    /** Returns the count stored in the cursor obtained from query execution. */
+    @VisibleForTesting
+    public synchronized long getCountFromCursor(Cursor cursor) {
+        long count = 0;
+        if (cursor != null && cursor.moveToFirst()) {
+            count = cursor.getInt(0);
+        }
+        return count;
+    }
+
+    /** Deletes Old Data and Overflow data in the db. */
+    @VisibleForTesting
+    public void deleteOverflowAndOldData(
+            String tableName, String overflowWhereClause, String oldDataWhereClause) {
+        deleteOverFlowData(tableName, overflowWhereClause);
+        deleteOldData(tableName, oldDataWhereClause);
+    }
+
+    protected void deleteOverFlowData(String tableName, String whereClause) {
+        String[] whereArgs = {Integer.toString(MAX_ENTRIES_LIMIT)};
+        delete(tableName, whereClause, whereArgs);
+    }
+
+    protected void deleteOldData(String tableName, String whereClause) {
+        String[] whereArgs = {getCutoffDate()};
+        delete(tableName, whereClause, whereArgs);
+    }
+
+    /** Utility function that performs deletion on the database table */
+    @VisibleForTesting
+    public synchronized void delete(String tableName, String whereClause, String[] whereArgs) {
+        try {
+            SQLiteDatabase db = getWritableDatabase();
+            db.delete(tableName, whereClause, whereArgs);
+        } catch (SQLException e) {
+            Rlog.e(TAG, "Sqlite Operation Error during deletion of Overflow data " + e);
+        }
+    }
+
+    private String getCutoffDate() {
+        Calendar cutoffDate = Calendar.getInstance();
+        cutoffDate.add(Calendar.MONTH, -1 * CUTOFF_MONTHS);
+        return DATE_FORMAT.format(cutoffDate.toInstant());
+    }
+}
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 65f3c4a..cb96c67 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -29,6 +29,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.telephony.AnomalyReporter;
 import android.telephony.SmsMessage;
 import android.text.TextUtils;
 
@@ -37,6 +38,7 @@
 
 import java.util.Iterator;
 import java.util.List;
+import java.util.UUID;
 import java.util.Locale;
 /**
  * Factory class, used for decoding raw byte arrays, received from baseband,
@@ -88,6 +90,11 @@
     private static final int MAX_GSM7_DEFAULT_CHARS = 239;
     private static final int MAX_UCS2_CHARS = 118;
 
+    // To Report Anomaly
+    public static final UUID NPE_WHEN_CALLED_SEND_CMD_PARAMS_UUID =
+            UUID.fromString("c2b85688-516e-11ee-be56-0242ac120002");
+    public static final String NPE_WHEN_CALLED_SEND_CMD_PARAMS_ERROR_MSG =
+            "mCaller[RilMessageDecoder] is Null when called SendCmdParams";
     /**
      * Returns a singleton instance of CommandParamsFactory
      * @param caller Class used for queuing raw ril messages, decoding them into
@@ -306,7 +313,13 @@
     }
 
     private void sendCmdParams(ResultCode resCode) {
-        mCaller.sendMsgParamsDecoded(resCode, mCmdParams);
+        if (mCaller != null) {
+            mCaller.sendMsgParamsDecoded(resCode, mCmdParams);
+        } else {
+            CatLog.e(this, "mCaller[RilMessageDecoder] is NULL");
+            AnomalyReporter.reportAnomaly(NPE_WHEN_CALLED_SEND_CMD_PARAMS_UUID,
+                    NPE_WHEN_CALLED_SEND_CMD_PARAMS_ERROR_MSG);
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
index 87591de..49f5c90 100644
--- a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
+++ b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
@@ -37,6 +37,7 @@
 import android.provider.Settings;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.NetworkRegistrationInfo;
+import android.telephony.NetworkRegistrationInfo.RegistrationState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -57,6 +58,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Recommend a data phone to use based on its availability.
@@ -102,6 +105,8 @@
     private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 4;
     /** Event indicates the switch state is stable, proceed to validation as the next step. */
     private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE = 5;
+    /** Event when subscriptions changed. */
+    private static final int EVENT_SUBSCRIPTIONS_CHANGED = 6;
 
     /** Fragment "key" argument passed thru {@link #SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS} */
     private static final String SETTINGS_EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
@@ -133,6 +138,12 @@
      */
     private long mAutoDataSwitchAvailabilityStabilityTimeThreshold = -1;
     /**
+     * The tolerated gap of score for auto data switch decision, larger than which the device will
+     * switch to the SIM with higher score. If 0, the device will always switch to the higher score
+     * SIM. If < 0, the network type and signal strength based auto switch is disabled.
+     */
+    private int mScoreTolerance = -1;
+    /**
      * {@code true} if requires ping test before switching preferred data modem; otherwise, switch
      * even if ping test fails.
      */
@@ -144,36 +155,51 @@
      */
     private int mAutoDataSwitchValidationMaxRetry;
 
+    /** The signal status of phones, where index corresponds to phone Id. */
     private @NonNull PhoneSignalStatus[] mPhonesSignalStatus;
+    /**
+     * The phone Id of the pending switching phone. Used for pruning frequent switch evaluation.
+     */
+    private int mSelectedTargetPhoneId = INVALID_PHONE_INDEX;
 
     /**
      * To track the signal status of a phone in order to evaluate whether it's a good candidate to
      * switch to.
      */
     private static class PhoneSignalStatus {
-        private @NonNull Phone mPhone;
-        private @NetworkRegistrationInfo.RegistrationState int mDataRegState =
-                NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+        /** The phone */
+        private final @NonNull Phone mPhone;
+        /** Data registration state of the phone */
+        private @RegistrationState int mDataRegState = NetworkRegistrationInfo
+                .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+        /** Current Telephony display info of the phone */
         private @NonNull TelephonyDisplayInfo mDisplayInfo;
+        /** Signal strength of the phone */
         private @NonNull SignalStrength mSignalStrength;
-
-        private int mScore;
-
+        /** {@code true} if this slot is listening for events. */
+        private boolean mListeningForEvents;
         private PhoneSignalStatus(@NonNull Phone phone) {
             this.mPhone = phone;
             this.mDisplayInfo = phone.getDisplayInfoController().getTelephonyDisplayInfo();
             this.mSignalStrength = phone.getSignalStrength();
         }
-        private int updateScore() {
-            // TODO: score = inservice? dcm.getscore() : 0
-            return mScore;
+
+        /**
+         * @return the current score of this phone. 0 indicates out of service and it will never be
+         * selected as the secondary data candidate.
+         */
+        private int getRatSignalScore() {
+            return isInService(mDataRegState)
+                    ? mPhone.getDataNetworkController().getDataConfigManager()
+                            .getAutoDataSwitchScore(mDisplayInfo, mSignalStrength) : 0;
         }
         @Override
         public String toString() {
-            return "{phoneId=" + mPhone.getPhoneId()
-                    + " score=" + mScore + " dataRegState="
+            return "{phone " + mPhone.getPhoneId()
+                    + " score=" + getRatSignalScore() + " dataRegState="
                     + NetworkRegistrationInfo.registrationStateToString(mDataRegState)
                     + " display=" + mDisplayInfo + " signalStrength=" + mSignalStrength.getLevel()
+                    + " listeningForEvents=" + mListeningForEvents
                     + "}";
 
         }
@@ -222,6 +248,8 @@
         readDeviceResourceConfig();
         int numActiveModems = PhoneFactory.getPhones().length;
         mPhonesSignalStatus = new PhoneSignalStatus[numActiveModems];
+        // Listening on all slots on boot up to make sure nothing missed. Later the tracking is
+        // pruned upon subscriptions changed.
         for (int phoneId = 0; phoneId < numActiveModems; phoneId++) {
             registerAllEventsForPhone(phoneId);
         }
@@ -236,16 +264,43 @@
         if (oldActiveModems == numActiveModems) return;
         // Dual -> Single
         for (int phoneId = numActiveModems; phoneId < oldActiveModems; phoneId++) {
-            Phone phone = mPhonesSignalStatus[phoneId].mPhone;
-            phone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged(this);
-            phone.getSignalStrengthController().unregisterForSignalStrengthChanged(this);
-            phone.getServiceStateTracker().unregisterForServiceStateChanged(this);
+            unregisterAllEventsForPhone(phoneId);
         }
         mPhonesSignalStatus = Arrays.copyOf(mPhonesSignalStatus, numActiveModems);
         // Signal -> Dual
         for (int phoneId = oldActiveModems; phoneId < numActiveModems; phoneId++) {
             registerAllEventsForPhone(phoneId);
         }
+        logl("onMultiSimConfigChanged: " + Arrays.toString(mPhonesSignalStatus));
+    }
+
+    /** Notify subscriptions changed. */
+    public void notifySubscriptionsChanged() {
+        sendEmptyMessage(EVENT_SUBSCRIPTIONS_CHANGED);
+    }
+
+    /**
+     * On subscription changed, register/unregister events on phone Id slot that has active/inactive
+     * sub to reduce unnecessary tracking.
+     */
+    private void onSubscriptionsChanged() {
+        Set<Integer> activePhoneIds = Arrays.stream(mSubscriptionManagerService
+                .getActiveSubIdList(true /*visibleOnly*/))
+                .map(mSubscriptionManagerService::getPhoneId)
+                .boxed()
+                .collect(Collectors.toSet());
+        // Track events only if there are at least two active visible subscriptions.
+        if (activePhoneIds.size() < 2) activePhoneIds.clear();
+        for (int phoneId = 0; phoneId < mPhonesSignalStatus.length; phoneId++) {
+            if (activePhoneIds.contains(phoneId)
+                    && !mPhonesSignalStatus[phoneId].mListeningForEvents) {
+                registerAllEventsForPhone(phoneId);
+            } else if (!activePhoneIds.contains(phoneId)
+                    && mPhonesSignalStatus[phoneId].mListeningForEvents) {
+                unregisterAllEventsForPhone(phoneId);
+            }
+        }
+        logl("onSubscriptionChanged: " + Arrays.toString(mPhonesSignalStatus));
     }
 
     /**
@@ -254,7 +309,7 @@
      */
     private void registerAllEventsForPhone(int phoneId) {
         Phone phone = PhoneFactory.getPhone(phoneId);
-        if (phone != null) {
+        if (phone != null && isActiveModemPhone(phoneId)) {
             mPhonesSignalStatus[phoneId] = new PhoneSignalStatus(phone);
             phone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged(
                     this, EVENT_DISPLAY_INFO_CHANGED, phoneId);
@@ -262,18 +317,36 @@
                     this, EVENT_SIGNAL_STRENGTH_CHANGED, phoneId);
             phone.getServiceStateTracker().registerForServiceStateChanged(this,
                     EVENT_SERVICE_STATE_CHANGED, phoneId);
+            mPhonesSignalStatus[phoneId].mListeningForEvents = true;
         } else {
             loge("Unexpected null phone " + phoneId + " when register all events");
         }
     }
 
     /**
+     * Unregister all tracking events for a phone.
+     * @param phoneId The phone to unregister for all events.
+     */
+    private void unregisterAllEventsForPhone(int phoneId) {
+        if (isActiveModemPhone(phoneId)) {
+            Phone phone = mPhonesSignalStatus[phoneId].mPhone;
+            phone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged(this);
+            phone.getSignalStrengthController().unregisterForSignalStrengthChanged(this);
+            phone.getServiceStateTracker().unregisterForServiceStateChanged(this);
+            mPhonesSignalStatus[phoneId].mListeningForEvents = false;
+        } else {
+            loge("Unexpected out of bound phone " + phoneId + " when unregister all events");
+        }
+    }
+
+    /**
      * Read the default device config from any default phone because the resource config are per
      * device. No need to register callback for the same reason.
      */
     private void readDeviceResourceConfig() {
         Phone phone = PhoneFactory.getDefaultPhone();
         DataConfigManager dataConfig = phone.getDataNetworkController().getDataConfigManager();
+        mScoreTolerance =  dataConfig.getAutoDataSwitchScoreTolerance();
         mRequirePingTestBeforeSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired();
         mAutoDataSwitchAvailabilityStabilityTimeThreshold =
                 dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
@@ -296,17 +369,25 @@
                 phoneId = (int) ar.userObj;
                 onDisplayInfoChanged(phoneId);
                 break;
+            case EVENT_SIGNAL_STRENGTH_CHANGED:
+                ar = (AsyncResult) msg.obj;
+                phoneId = (int) ar.userObj;
+                onSignalStrengthChanged(phoneId);
+                break;
             case EVENT_EVALUATE_AUTO_SWITCH:
                 int reason = (int) msg.obj;
                 onEvaluateAutoDataSwitch(reason);
                 break;
             case EVENT_MEETS_AUTO_DATA_SWITCH_STATE:
                 int targetPhoneId = msg.arg1;
-                boolean needValidation = (boolean) msg.obj;
+                boolean needValidation = msg.arg2 == 1;
                 log("require validation on phone " + targetPhoneId
                         + (needValidation ? "" : " no") + " need to pass");
                 mPhoneSwitcherCallback.onRequireValidation(targetPhoneId, needValidation);
                 break;
+            case EVENT_SUBSCRIPTIONS_CHANGED:
+                onSubscriptionsChanged();
+                break;
             default:
                 loge("Unexpected event " + msg.what);
         }
@@ -317,7 +398,7 @@
      */
     private void onRegistrationStateChanged(int phoneId) {
         Phone phone = PhoneFactory.getPhone(phoneId);
-        if (phone != null) {
+        if (phone != null && isActiveModemPhone(phoneId)) {
             int oldRegState = mPhonesSignalStatus[phoneId].mDataRegState;
             int newRegState = phone.getServiceState()
                     .getNetworkRegistrationInfo(
@@ -326,44 +407,93 @@
                     .getRegistrationState();
             if (newRegState != oldRegState) {
                 mPhonesSignalStatus[phoneId].mDataRegState = newRegState;
-                log("onRegistrationStateChanged: phone " + phoneId + " "
-                        + NetworkRegistrationInfo.registrationStateToString(oldRegState)
-                        + " -> "
-                        + NetworkRegistrationInfo.registrationStateToString(newRegState));
-                evaluateAutoDataSwitch(EVALUATION_REASON_REGISTRATION_STATE_CHANGED);
-            } else {
-                log("onRegistrationStateChanged: no change.");
+                if (isInService(oldRegState) != isInService(newRegState)
+                        || isHomeService(oldRegState) != isHomeService(newRegState)) {
+                    log("onRegistrationStateChanged: phone " + phoneId + " "
+                            + NetworkRegistrationInfo.registrationStateToString(oldRegState)
+                            + " -> "
+                            + NetworkRegistrationInfo.registrationStateToString(newRegState));
+                    evaluateAutoDataSwitch(EVALUATION_REASON_REGISTRATION_STATE_CHANGED);
+                }
             }
         } else {
             loge("Unexpected null phone " + phoneId + " upon its registration state changed");
         }
     }
 
-    /**
-     * @return {@code true} if the phone state is considered in service.
-     */
-    private boolean isInService(@NetworkRegistrationInfo.RegistrationState int dataRegState) {
+    /** @return {@code true} if the phone state is considered in service. */
+    private static boolean isInService(@RegistrationState int dataRegState) {
         return dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME
                 || dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
     }
 
+    /** @return {@code true} if the phone state is in home service. */
+    private static boolean isHomeService(@RegistrationState int dataRegState) {
+        return dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+    }
+
     /**
      * Called when {@link TelephonyDisplayInfo} changed. This can happen when network types or
      * override network types (5G NSA, 5G MMWAVE) change.
+     * @param phoneId The phone that changed.
      */
     private void onDisplayInfoChanged(int phoneId) {
         Phone phone = PhoneFactory.getPhone(phoneId);
-        if (phone != null) {
+        if (phone != null && isActiveModemPhone(phoneId)) {
             TelephonyDisplayInfo displayInfo = phone.getDisplayInfoController()
                     .getTelephonyDisplayInfo();
-            //TODO(b/260928808)
-            log("onDisplayInfoChanged:" + displayInfo);
+            mPhonesSignalStatus[phoneId].mDisplayInfo = displayInfo;
+            if (getHigherScoreCandidatePhoneId() != mSelectedTargetPhoneId) {
+                log("onDisplayInfoChanged: phone " + phoneId + " " + displayInfo);
+                evaluateAutoDataSwitch(EVALUATION_REASON_DISPLAY_INFO_CHANGED);
+            }
         } else {
             loge("Unexpected null phone " + phoneId + " upon its display info changed");
         }
     }
 
     /**
+     * Called when {@link SignalStrength} changed.
+     * @param phoneId The phone that changed.
+     */
+    private void onSignalStrengthChanged(int phoneId) {
+        Phone phone = PhoneFactory.getPhone(phoneId);
+        if (phone != null && isActiveModemPhone(phoneId)) {
+            SignalStrength newSignalStrength = phone.getSignalStrength();
+            SignalStrength oldSignalStrength = mPhonesSignalStatus[phoneId].mSignalStrength;
+            if (oldSignalStrength.getLevel() != newSignalStrength.getLevel()) {
+                mPhonesSignalStatus[phoneId].mSignalStrength = newSignalStrength;
+                if (getHigherScoreCandidatePhoneId() != mSelectedTargetPhoneId) {
+                    log("onSignalStrengthChanged: phone " + phoneId + " "
+                            + oldSignalStrength.getLevel() + "->" + newSignalStrength.getLevel());
+                    evaluateAutoDataSwitch(EVALUATION_REASON_SIGNAL_STRENGTH_CHANGED);
+                }
+            }
+        } else {
+            loge("Unexpected null phone " + phoneId + " upon its signal strength changed");
+        }
+    }
+
+    /**
+     * Called as a preliminary check for the frequent signal/display info change.
+     * @return The phone Id if found a candidate phone with higher signal score.
+     */
+    private int getHigherScoreCandidatePhoneId() {
+        int preferredPhoneId = mPhoneSwitcher.getPreferredDataPhoneId();
+        if (isActiveModemPhone(preferredPhoneId)) {
+            int currentScore = mPhonesSignalStatus[preferredPhoneId].getRatSignalScore();
+            for (int phoneId = 0; phoneId < mPhonesSignalStatus.length; phoneId++) {
+                int candidateScore = mPhonesSignalStatus[phoneId].getRatSignalScore();
+                if (phoneId != preferredPhoneId
+                        && (candidateScore - currentScore) > mScoreTolerance) {
+                    return phoneId;
+                }
+            }
+        }
+        return INVALID_PHONE_INDEX;
+    }
+
+    /**
      * Schedule for auto data switch evaluation.
      * @param reason The reason for the evaluation.
      */
@@ -388,10 +518,7 @@
         if (mAutoDataSwitchAvailabilityStabilityTimeThreshold < 0) return;
         int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId();
         // check is valid DSDS
-        if (!isActiveSubId(defaultDataSubId) || mSubscriptionManagerService
-                .getActiveSubIdList(true).length <= 1) {
-            return;
-        }
+        if (mSubscriptionManagerService.getActiveSubIdList(true).length < 2) return;
         Phone defaultDataPhone = PhoneFactory.getPhone(mSubscriptionManagerService.getPhoneId(
                 defaultDataSubId));
         if (defaultDataPhone == null) {
@@ -401,13 +528,16 @@
         }
         int defaultDataPhoneId = defaultDataPhone.getPhoneId();
         int preferredPhoneId = mPhoneSwitcher.getPreferredDataPhoneId();
-        log("onEvaluateAutoDataSwitch: defaultPhoneId: " + defaultDataPhoneId
-                + " preferredPhoneId: " + preferredPhoneId
-                + " reason: " + evaluationReasonToString(reason));
+        StringBuilder debugMessage = new StringBuilder("onEvaluateAutoDataSwitch:");
+        debugMessage.append(" defaultPhoneId: ").append(defaultDataPhoneId)
+                .append(" preferredPhoneId: ").append(preferredPhoneId)
+                .append(", reason: ").append(evaluationReasonToString(reason));
         if (preferredPhoneId == defaultDataPhoneId) {
             // on default data sub
-            int candidatePhoneId = getSwitchCandidatePhoneId(defaultDataPhoneId);
+            int candidatePhoneId = getSwitchCandidatePhoneId(defaultDataPhoneId, debugMessage);
+            log(debugMessage.toString());
             if (candidatePhoneId != INVALID_PHONE_INDEX) {
+                mSelectedTargetPhoneId = candidatePhoneId;
                 startStabilityCheck(candidatePhoneId, mRequirePingTestBeforeSwitch);
             } else {
                 cancelAnyPendingSwitch();
@@ -415,90 +545,138 @@
         } else {
             // on backup data sub
             Phone backupDataPhone = PhoneFactory.getPhone(preferredPhoneId);
-            if (backupDataPhone == null) {
-                loge("onEvaluateAutoDataSwitch: Unexpected null phone " + preferredPhoneId
-                        + " as the current active data phone");
+            if (backupDataPhone == null || !isActiveModemPhone(preferredPhoneId)) {
+                loge(debugMessage.append(" Unexpected null phone ").append(preferredPhoneId)
+                        .append(" as the current active data phone").toString());
                 return;
             }
 
             if (!defaultDataPhone.isUserDataEnabled() || !backupDataPhone.isDataAllowed()) {
-                // immediately switch back if user disabled setting changes
                 mPhoneSwitcherCallback.onRequireImmediatelySwitchToPhone(DEFAULT_PHONE_INDEX,
                         EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+                log(debugMessage.append(", immediately back to default as user turns off settings")
+                        .toString());
                 return;
             }
 
+            boolean backToDefault = false;
+            boolean needValidation = true;
+
             if (mDefaultNetworkIsOnNonCellular) {
-                log("onEvaluateAutoDataSwitch: Default network is active on nonCellular transport");
-                startStabilityCheck(DEFAULT_PHONE_INDEX, false);
-                return;
+                log(debugMessage.append(", back to default as default network")
+                        .append(" is active on nonCellular transport").toString());
+                backToDefault = true;
+                needValidation = false;
+            } else if (!isHomeService(mPhonesSignalStatus[preferredPhoneId].mDataRegState)) {
+                log(debugMessage.append(", back to default as backup phone lost HOME registration")
+                        .toString());
+                backToDefault = true;
+                needValidation = false;
+            } else if (isRatSignalStrengthBasedSwitchEnabled()) {
+                int defaultScore = mPhonesSignalStatus[defaultDataPhoneId].getRatSignalScore();
+                int currentScore = mPhonesSignalStatus[preferredPhoneId].getRatSignalScore();
+                if ((defaultScore - currentScore) > mScoreTolerance) {
+                    log(debugMessage
+                            .append(", back to default as default phone has higher score ")
+                            .append(defaultScore).append(" versus current ")
+                            .append(currentScore).toString());
+                    backToDefault = true;
+                    needValidation = mRequirePingTestBeforeSwitch;
+                }
+            } else if (isInService(mPhonesSignalStatus[defaultDataPhoneId].mDataRegState)) {
+                log(debugMessage.append(", back to default as the default is back to service ")
+                        .toString());
+                backToDefault = true;
+                needValidation = mRequirePingTestBeforeSwitch;
             }
 
-            if (mPhonesSignalStatus[preferredPhoneId].mDataRegState
-                    != NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
-                // backup phone lost its HOME registration
-                startStabilityCheck(DEFAULT_PHONE_INDEX, false);
-                return;
+            if (backToDefault) {
+                mSelectedTargetPhoneId = defaultDataPhoneId;
+                startStabilityCheck(DEFAULT_PHONE_INDEX, needValidation);
+            } else {
+                // cancel any previous attempts of switching back to default phone
+                cancelAnyPendingSwitch();
             }
-
-            if (isInService(mPhonesSignalStatus[defaultDataPhoneId].mDataRegState)) {
-                // default phone is back to service
-                startStabilityCheck(DEFAULT_PHONE_INDEX, mRequirePingTestBeforeSwitch);
-                return;
-            }
-
-            // cancel any previous attempts of switching back to default phone
-            cancelAnyPendingSwitch();
         }
     }
 
     /**
      * Called when consider switching from primary default data sub to another data sub.
+     * @param defaultPhoneId The default data phone
+     * @param debugMessage Debug message.
      * @return the target subId if a suitable candidate is found, otherwise return
      * {@link SubscriptionManager#INVALID_PHONE_INDEX}
      */
-    private int getSwitchCandidatePhoneId(int defaultPhoneId) {
+    private int getSwitchCandidatePhoneId(int defaultPhoneId, @NonNull StringBuilder debugMessage) {
         Phone defaultDataPhone = PhoneFactory.getPhone(defaultPhoneId);
         if (defaultDataPhone == null) {
-            log("getSwitchCandidatePhoneId: no sim loaded");
+            debugMessage.append(", no candidate as no sim loaded");
             return INVALID_PHONE_INDEX;
         }
 
         if (!defaultDataPhone.isUserDataEnabled()) {
-            log("getSwitchCandidatePhoneId: user disabled data");
+            debugMessage.append(", no candidate as user disabled mobile data");
             return INVALID_PHONE_INDEX;
         }
 
         if (mDefaultNetworkIsOnNonCellular) {
-            // Exists other active default transport
-            log("getSwitchCandidatePhoneId: Default network is active on non-cellular transport");
+            debugMessage.append(", no candidate as default network is active")
+                    .append(" on non-cellular transport");
             return INVALID_PHONE_INDEX;
         }
 
         // check whether primary and secondary signal status are worth switching
-        if (isInService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
-            log("getSwitchCandidatePhoneId: DDS is in service");
+        if (!isRatSignalStrengthBasedSwitchEnabled()
+                && isInService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
+            debugMessage.append(", no candidate as default phone is in service");
             return INVALID_PHONE_INDEX;
         }
         for (int phoneId = 0; phoneId < mPhonesSignalStatus.length; phoneId++) {
-            if (phoneId != defaultPhoneId) {
-                // the alternative phone must have HOME availability
-                if (mPhonesSignalStatus[phoneId].mDataRegState
-                        == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
-                    log("getSwitchCandidatePhoneId: found phone " + phoneId
-                            + " in HOME service");
-                    Phone secondaryDataPhone = PhoneFactory.getPhone(phoneId);
-                    if (secondaryDataPhone != null && // check auto switch feature enabled
-                            secondaryDataPhone.isDataAllowed()) {
+            if (phoneId != defaultPhoneId
+                    // the alternative phone must have HOME availability
+                    && isHomeService(mPhonesSignalStatus[phoneId].mDataRegState)) {
+                Phone secondaryDataPhone = null;
+                debugMessage.append(", found phone ").append(phoneId).append(" in HOME reg");
+
+                if (isRatSignalStrengthBasedSwitchEnabled()) {
+                    // Use score if RAT/signal strength based switch is enabled.
+                    int defaultScore = mPhonesSignalStatus[defaultPhoneId].getRatSignalScore();
+                    int candidateScore = mPhonesSignalStatus[phoneId].getRatSignalScore();
+                    if ((candidateScore - defaultScore) > mScoreTolerance) {
+                        debugMessage.append(" with higher score ").append(candidateScore)
+                                .append(" versus current ").append(defaultScore);
+                        secondaryDataPhone = PhoneFactory.getPhone(phoneId);
+                    } else {
+                        debugMessage.append(", but its score ").append(candidateScore)
+                                .append(" doesn't meet the bar to switch given the current ")
+                                .append(defaultScore);
+                    }
+                } else {
+                    // Only OOS/in service switch is enabled.
+                    secondaryDataPhone = PhoneFactory.getPhone(phoneId);
+                }
+                if (secondaryDataPhone != null) {
+                    // check auto switch feature enabled
+                    if (secondaryDataPhone.isDataAllowed()) {
                         return phoneId;
+                    } else {
+                        debugMessage.append(", but its data is not allowed");
                     }
                 }
             }
         }
+        debugMessage.append(", found no qualified candidate.");
         return INVALID_PHONE_INDEX;
     }
 
     /**
+     * @return {@code true} If the feature of switching base on RAT and signal strength is enabled.
+     */
+    private boolean isRatSignalStrengthBasedSwitchEnabled() {
+        return mScoreTolerance >= 0;
+    }
+
+    /**
      * Called when the current environment suits auto data switch.
      * Start pre-switch validation if the current environment suits auto data switch for
      * {@link #mAutoDataSwitchAvailabilityStabilityTimeThreshold} MS.
@@ -508,10 +686,12 @@
     private void startStabilityCheck(int targetPhoneId, boolean needValidation) {
         log("startAutoDataSwitchStabilityCheck: targetPhoneId=" + targetPhoneId
                 + " needValidation=" + needValidation);
-        if (!hasMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, needValidation)) {
+        String combinationIdentifier = targetPhoneId + "" + needValidation;
+        if (!hasEqualMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, combinationIdentifier)) {
+            removeMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE);
             sendMessageDelayed(obtainMessage(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, targetPhoneId,
-                            0/*placeholder*/,
-                            needValidation),
+                            needValidation ? 1 : 0,
+                            combinationIdentifier),
                     mAutoDataSwitchAvailabilityStabilityTimeThreshold);
         }
     }
@@ -566,6 +746,7 @@
      * Cancel any auto switch attempts when the current environment is not suitable for auto switch.
      */
     private void cancelAnyPendingSwitch() {
+        mSelectedTargetPhoneId = INVALID_PHONE_INDEX;
         resetFailedCount();
         removeMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE);
         mPhoneSwitcherCallback.onRequireCancelAnyPendingAutoSwitchValidation();
@@ -651,6 +832,14 @@
     }
 
     /**
+     * @param phoneId The phone Id to check.
+     * @return {@code true} if the phone Id is an active modem.
+     */
+    private boolean isActiveModemPhone(int phoneId) {
+        return phoneId >= 0 && phoneId < mPhonesSignalStatus.length;
+    }
+
+    /**
      * Log debug messages.
      * @param s debug messages
      */
@@ -686,11 +875,13 @@
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
         pw.println("AutoDataSwitchController:");
         pw.increaseIndent();
+        pw.println("mScoreTolerance=" + mScoreTolerance);
         pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry
                 + " mAutoSwitchValidationFailedCount=" + mAutoSwitchValidationFailedCount);
         pw.println("mRequirePingTestBeforeDataSwitch=" + mRequirePingTestBeforeSwitch);
         pw.println("mAutoDataSwitchAvailabilityStabilityTimeThreshold="
                 + mAutoDataSwitchAvailabilityStabilityTimeThreshold);
+        pw.println("mSelectedTargetPhoneId=" + mSelectedTargetPhoneId);
         pw.increaseIndent();
         for (PhoneSignalStatus status: mPhonesSignalStatus) {
             pw.println(status);
diff --git a/src/java/com/android/internal/telephony/data/CellularDataService.java b/src/java/com/android/internal/telephony/data/CellularDataService.java
index c5923aa..80d6b53 100644
--- a/src/java/com/android/internal/telephony/data/CellularDataService.java
+++ b/src/java/com/android/internal/telephony/data/CellularDataService.java
@@ -167,6 +167,7 @@
                 boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties,
                 int pduSessionId, NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
                 boolean matchAllRuleAllowed, DataServiceCallback callback) {
+            // TODO: remove isRoaming parameter
             if (DBG) log("setupDataCall " + getSlotIndex());
 
             Message message = null;
@@ -177,9 +178,9 @@
                 mCallbackMap.put(message, callback);
             }
 
-            mPhone.mCi.setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming,
-                    reason, linkProperties, pduSessionId, sliceInfo, trafficDescriptor,
-                    matchAllRuleAllowed, message);
+            mPhone.mCi.setupDataCall(accessNetworkType, dataProfile, allowRoaming, reason,
+                    linkProperties, pduSessionId, sliceInfo, trafficDescriptor, matchAllRuleAllowed,
+                    message);
         }
 
         @Override
@@ -199,7 +200,8 @@
 
         @Override
         public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
-                                        DataServiceCallback callback) {
+                DataServiceCallback callback) {
+            // TODO: remove isRoaming parameter
             if (DBG) log("setInitialAttachApn " + getSlotIndex());
 
             Message message = null;
@@ -210,12 +212,13 @@
                 mCallbackMap.put(message, callback);
             }
 
-            mPhone.mCi.setInitialAttachApn(dataProfile, isRoaming, message);
+            mPhone.mCi.setInitialAttachApn(dataProfile, message);
         }
 
         @Override
         public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
-                                   DataServiceCallback callback) {
+                DataServiceCallback callback) {
+            // TODO: remove isRoaming parameter
             if (DBG) log("setDataProfile " + getSlotIndex());
 
             Message message = null;
@@ -226,7 +229,7 @@
                 mCallbackMap.put(message, callback);
             }
 
-            mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[dps.size()]), isRoaming, message);
+            mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[dps.size()]), message);
         }
 
         @Override
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java
index f7fe4ad..22ad593 100644
--- a/src/java/com/android/internal/telephony/data/DataConfigManager.java
+++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -213,8 +213,8 @@
             "anomaly_network_handover_timeout";
     /** DeviceConfig key of anomaly report: True for enabling APN config invalidity detection */
     private static final String KEY_ANOMALY_APN_CONFIG_ENABLED = "anomaly_apn_config_enabled";
-    /** Invalid auto data switch score. */
-    private static final int INVALID_AUTO_DATA_SWITCH_SCORE = -1;
+    /** Placeholder indicating missing Auto data switch score config, meaning out of service. */
+    private static final int OUT_OF_SERVICE_AUTO_DATA_SWITCH_SCORE = 0;
     /** Anomaly report thresholds for frequent setup data call failure. */
     private EventFrequency mSetupDataCallAnomalyReportThreshold;
 
@@ -326,7 +326,7 @@
 
         // Register for device config update
         DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_TELEPHONY, this::post,
+                DeviceConfig.NAMESPACE_TELEPHONY, Runnable::run,
                 properties -> {
                     if (TextUtils.equals(DeviceConfig.NAMESPACE_TELEPHONY,
                             properties.getNamespace())) {
@@ -978,12 +978,14 @@
      * @param displayInfo The displayed network info.
      * @param signalStrength The signal strength.
      * @return Score base on network type and signal strength to inform auto data switch decision.
+     * The min score is {@link #OUT_OF_SERVICE_AUTO_DATA_SWITCH_SCORE} indicating missing config.
      */
     public int getAutoDataSwitchScore(@NonNull TelephonyDisplayInfo displayInfo,
             @NonNull SignalStrength signalStrength) {
         int[] scores = mAutoDataSwitchNetworkTypeSignalMap.get(
                 getDataConfigNetworkType(displayInfo));
-        return scores != null ? scores[signalStrength.getLevel()] : INVALID_AUTO_DATA_SWITCH_SCORE;
+        return scores != null ? scores[signalStrength.getLevel()]
+                : OUT_OF_SERVICE_AUTO_DATA_SWITCH_SCORE;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/DataEvaluation.java b/src/java/com/android/internal/telephony/data/DataEvaluation.java
index 2ba3fe4..310f72a 100644
--- a/src/java/com/android/internal/telephony/data/DataEvaluation.java
+++ b/src/java/com/android/internal/telephony/data/DataEvaluation.java
@@ -145,13 +145,18 @@
     }
 
     /**
-     * Check if only one disallowed reason prevent data connection.
+     * Check if only the given reasons present in the disallowed reasons if there are any.
      *
-     * @param reason The given reason to check
-     * @return {@code true} if the given reason is the only one that prevents data connection
+     * @param reasons The given reasons to check
+     * @return {@code true} if the disallowed reasons contain no disallowed reasons other than the
+     * given reasons.
      */
-    public boolean containsOnly(DataDisallowedReason reason) {
-        return mDataDisallowedReasons.size() == 1 && contains(reason);
+    public boolean containsOnly(DataDisallowedReason... reasons) {
+        int matched = 0;
+        for (DataDisallowedReason requestedReason : reasons) {
+            if (mDataDisallowedReasons.contains(requestedReason)) matched++;
+        }
+        return matched == mDataDisallowedReasons.size();
     }
 
     /**
@@ -290,6 +295,8 @@
         SIM_NOT_READY(true),
         /** Concurrent voice and data is not allowed. */
         CONCURRENT_VOICE_DATA_NOT_ALLOWED(true),
+        /** Service option not supported. */
+        SERVICE_OPTION_NOT_SUPPORTED(true),
         /** Carrier notified data should be restricted. */
         DATA_RESTRICTED_BY_NETWORK(true),
         /** Radio power is off (i.e. airplane mode on) */
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 6ba251b..6b410ce 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -74,6 +74,7 @@
 import android.telephony.data.DataService;
 import android.telephony.data.DataServiceCallback;
 import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.Qos;
 import android.telephony.data.QosBearerSession;
 import android.telephony.data.TrafficDescriptor;
 import android.telephony.data.TrafficDescriptor.OsAppId;
@@ -95,6 +96,7 @@
 import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
 import com.android.internal.telephony.data.DataRetryManager.DataHandoverRetryEntry;
 import com.android.internal.telephony.data.DataRetryManager.DataRetryEntry;
+import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
 import com.android.internal.telephony.data.LinkBandwidthEstimator.LinkBandwidthEstimatorCallback;
 import com.android.internal.telephony.data.TelephonyNetworkAgent.TelephonyNetworkAgentCallback;
 import com.android.internal.telephony.metrics.DataCallSessionStats;
@@ -278,6 +280,7 @@
                     TEAR_DOWN_REASON_RAT_NOT_ALLOWED,
                     TEAR_DOWN_REASON_ROAMING_DISABLED,
                     TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED,
+                    TEAR_DOWN_REASON_SERVICE_OPTION_NOT_SUPPORTED,
                     TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY,
                     TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER,
                     TEAR_DOWN_REASON_DATA_STALL,
@@ -329,7 +332,8 @@
     /** Data network tear down due to concurrent voice/data not allowed. */
     public static final int TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED = 8;
 
-
+    /** Data network tear down due to service option is not supported. */
+    public static final int TEAR_DOWN_REASON_SERVICE_OPTION_NOT_SUPPORTED = 9;
 
     /** Data network tear down due to data service unbound. */
     public static final int TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY = 10;
@@ -493,7 +497,7 @@
     private final int mSubId;
 
     /** The network score of this network. */
-    private int mNetworkScore;
+    private @NonNull NetworkScore mNetworkScore;
 
     /**
      * Indicates that
@@ -556,6 +560,9 @@
     private final @NonNull DataNetworkController.DataNetworkControllerCallback
             mDataNetworkControllerCallback;
 
+    /** Data settings manager callback. */
+    private @NonNull DataSettingsManagerCallback mDataSettingsManagerCallback;
+
     /** Data config manager. */
     private final @NonNull DataConfigManager mDataConfigManager;
 
@@ -681,6 +688,9 @@
     /** The QOS bearer sessions. */
     private final @NonNull List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
 
+    /** The QOS for the Default Bearer, should be non-null on LTE and NR */
+    private @Nullable Qos mDefaultQos;
+
     /**
      * The UIDs of packages that have carrier privilege.
      */
@@ -1024,10 +1034,18 @@
                 mPhone.getPhoneId());
         final NetworkProvider provider = (null == factory) ? null : factory.getProvider();
 
-        mNetworkScore = getNetworkScore();
+        // Always prefer IWLAN network for MMS designated network.
+        // TODO(b/293656884) Proper use of primary transport to avoid conflicting with DSDA.
+        boolean isPreferred = mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
+                && getApnTypeNetworkCapability() == NetworkCapabilities.NET_CAPABILITY_MMS;
+
+        mNetworkScore = new NetworkScore.Builder().setTransportPrimary(isPreferred)
+                .setKeepConnectedReason(isHandoverInProgress()
+                        ? NetworkScore.KEEP_CONNECTED_FOR_HANDOVER
+                        : NetworkScore.KEEP_CONNECTED_NONE).build();
+
         return new TelephonyNetworkAgent(mPhone, getHandler().getLooper(), this,
-                new NetworkScore.Builder().setLegacyInt(mNetworkScore).build(),
-                configBuilder.build(), provider,
+                mNetworkScore, configBuilder.build(), provider,
                 new TelephonyNetworkAgentCallback(getHandler()::post) {
                     @Override
                     public void onValidationStatus(@ValidationStatus int status,
@@ -1058,6 +1076,34 @@
             mRil.registerForPcoData(getHandler(), EVENT_PCO_DATA_RECEIVED, null);
 
             mDataConfigManager.registerCallback(mDataConfigManagerCallback);
+
+            mDataSettingsManagerCallback = new DataSettingsManagerCallback(getHandler()::post) {
+                @Override
+                public void onDataEnabledChanged(boolean enabled,
+                        @TelephonyManager.DataEnabledChangedReason int reason,
+                        @NonNull String callingPackage) {
+                    if (enabled) {
+                        // The NOT_RESTRICTED capability might be changed after data enabled. We
+                        // need to update the capabilities again.
+                        log("Data enabled. update network capabilities.");
+                        updateNetworkCapabilities();
+                    }
+                }
+
+                @Override
+                public void onDataRoamingEnabledChanged(boolean enabled) {
+                    if (enabled) {
+                        // The NOT_RESTRICTED capability might be changed after data roaming
+                        // enabled. We need to update the capabilities again.
+                        log("Data roaming enabled. update network capabilities.");
+                        updateNetworkCapabilities();
+                    }
+                }
+            };
+
+            mDataNetworkController.getDataSettingsManager()
+                    .registerCallback(mDataSettingsManagerCallback);
+
             mPhone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged(
                     getHandler(), EVENT_DISPLAY_INFO_CHANGED, null);
             mPhone.getServiceStateTracker().registerForServiceStateChanged(getHandler(),
@@ -1126,6 +1172,8 @@
             mPhone.getServiceStateTracker().unregisterForServiceStateChanged(getHandler());
             mPhone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged(
                     getHandler());
+            mDataNetworkController.getDataSettingsManager()
+                    .unregisterCallback(mDataSettingsManagerCallback);
             mRil.unregisterForPcoData(getHandler());
             mDataConfigManager.unregisterCallback(mDataConfigManagerCallback);
         }
@@ -1157,13 +1205,13 @@
                 }
                 case EVENT_ATTACH_NETWORK_REQUEST: {
                     onAttachNetworkRequests((NetworkRequestList) msg.obj);
-                    updateNetworkScore();
+                    updateNetworkScore(isHandoverInProgress());
                     break;
                 }
                 case EVENT_DETACH_NETWORK_REQUEST: {
                     onDetachNetworkRequest((TelephonyNetworkRequest) msg.obj,
                             msg.arg1 != 0 /* shouldRetry */);
-                    updateNetworkScore();
+                    updateNetworkScore(isHandoverInProgress());
                     break;
                 }
                 case EVENT_DETACH_ALL_NETWORK_REQUESTS: {
@@ -1238,6 +1286,8 @@
      * @see DataNetwork for the state machine diagram.
      */
     private final class ConnectingState extends State {
+        /** Used for checking setup response IP mismatch. */
+        @NetworkRegistrationInfo.RegistrationState private int mRegStateWhenSetup;
         @Override
         public void enter() {
             sendMessageDelayed(EVENT_STUCK_IN_TRANSIENT_STATE,
@@ -1305,11 +1355,149 @@
                     mFailCause = DataFailCause.NO_RETRY_FAILURE;
                     transitionTo(mDisconnectedState);
                     break;
+                case EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE:
+                    int responseCode = msg.arg1;
+                    onDeactivateResponse(responseCode);
+                    break;
                 default:
                     return NOT_HANDLED;
             }
             return HANDLED;
         }
+
+        /**
+         * Setup a data network.
+         */
+        private void setupData() {
+            int dataNetworkType = getDataNetworkType();
+
+            NetworkRegistrationInfo nri = getNetworkRegistrationInfo();
+            mRegStateWhenSetup = nri != null
+                    ? nri.getNetworkRegistrationState()
+                    : NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
+            // We need to use the actual modem roaming state instead of the framework roaming state
+            // here. This flag is only passed down to ril_service for picking the correct protocol
+            // (for old modem backward compatibility).
+            boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration();
+
+            // Set this flag to true if the user turns on data roaming. Or if we override the
+            // roaming state in framework, we should set this flag to true as well so the modem will
+            // not reject the data call setup (because the modem thinks the device is roaming).
+            boolean allowRoaming = mPhone.getDataRoamingEnabled()
+                    || (isModemRoaming && (!mPhone.getServiceState().getDataRoaming()
+                    /*|| isUnmeteredUseOnly()*/));
+
+            TrafficDescriptor trafficDescriptor = mDataProfile.getTrafficDescriptor();
+            final boolean matchAllRuleAllowed = trafficDescriptor == null
+                    || !TextUtils.isEmpty(trafficDescriptor.getDataNetworkName())
+                    // Both OsAppId and APN name are null. This helps for modem to handle when we
+                    // are on 5G or LTE with URSP support in falling back to default network.
+                    || (TextUtils.isEmpty(trafficDescriptor.getDataNetworkName())
+                    && trafficDescriptor.getOsAppId() == null);
+
+            int accessNetwork = DataUtils.networkTypeToAccessNetworkType(dataNetworkType);
+
+            mDataServiceManagers.get(mTransport)
+                    .setupDataCall(accessNetwork, mDataProfile, isModemRoaming, allowRoaming,
+                            DataService.REQUEST_REASON_NORMAL, null, mPduSessionId, null,
+                            trafficDescriptor, matchAllRuleAllowed,
+                            obtainMessage(EVENT_SETUP_DATA_NETWORK_RESPONSE));
+
+            int apnTypeBitmask = mDataProfile.getApnSetting() != null
+                    ? mDataProfile.getApnSetting().getApnTypeBitmask() : ApnSetting.TYPE_NONE;
+            mDataCallSessionStats.onSetupDataCall(apnTypeBitmask);
+
+            logl("setupData: accessNetwork="
+                    + AccessNetworkType.toString(accessNetwork) + ", " + mDataProfile
+                    + ", isModemRoaming=" + isModemRoaming + ", allowRoaming=" + allowRoaming
+                    + ", PDU session id=" + mPduSessionId + ", matchAllRuleAllowed="
+                    + matchAllRuleAllowed);
+            TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(),
+                    ServiceState.networkTypeToRilRadioTechnology(dataNetworkType),
+                    mDataProfile.getProfileId(), mDataProfile.getApn(),
+                    mDataProfile.getProtocolType());
+        }
+
+        /**
+         * Called when receiving setup data network response from the data service.
+         *
+         * @param resultCode The result code.
+         * @param response The response.
+         */
+        private void onSetupResponse(@DataServiceCallback.ResultCode int resultCode,
+                @Nullable DataCallResponse response) {
+            logl("onSetupResponse: resultCode=" + DataServiceCallback.resultCodeToString(resultCode)
+                    + ", response=" + response);
+            mFailCause = getFailCauseFromDataCallResponse(resultCode, response);
+            validateDataCallResponse(response, mRegStateWhenSetup);
+            if (mFailCause == DataFailCause.NONE) {
+                DataNetwork dataNetwork = mDataNetworkController.getDataNetworkByInterface(
+                        response.getInterfaceName());
+                if (dataNetwork != null) {
+                    logl("Interface " + response.getInterfaceName() + " has been already used by "
+                            + dataNetwork + ". Silently tear down now.");
+                    // If this is a pre-5G data setup, that means APN database has some problems.
+                    // For example, different APN settings have the same APN name.
+                    if (response.getTrafficDescriptors().isEmpty() && dataNetwork.isConnected()) {
+                        reportAnomaly("Duplicate network interface " + response.getInterfaceName()
+                                + " detected.", "62f66e7e-8d71-45de-a57b-dc5c78223fd5");
+                    }
+
+                    // Do not actually invoke onTearDown, otherwise the existing data network will
+                    // be torn down.
+                    mRetryDelayMillis = DataCallResponse.RETRY_DURATION_UNDEFINED;
+                    mFailCause = DataFailCause.NO_RETRY_FAILURE;
+                    transitionTo(mDisconnectedState);
+                    return;
+                }
+
+                updateDataNetwork(response);
+
+                // TODO: Evaluate all network requests and see if each request still can be
+                //  satisfied.
+                //  For requests that can't be satisfied anymore, we need to put them back to the
+                //  unsatisfied pool. If none of network requests can be satisfied, then there is no
+                //  need to mark network agent connected. Just silently deactivate the data network.
+                if (mAttachedNetworkRequestList.size() == 0) {
+                    log("Tear down the network since there is no live network request.");
+                    // Directly call onTearDown here. Calling tearDown will cause deadlock because
+                    // EVENT_TEAR_DOWN_NETWORK is deferred until state machine enters connected
+                    // state, which will never happen in this case.
+                    onTearDown(TEAR_DOWN_REASON_NO_LIVE_REQUEST);
+                    return;
+                }
+
+                if (mVcnManager != null && mVcnManager.applyVcnNetworkPolicy(mNetworkCapabilities,
+                        mLinkProperties).isTeardownRequested()) {
+                    log("VCN service requested to tear down the network.");
+                    // Directly call onTearDown here. Calling tearDown will cause deadlock because
+                    // EVENT_TEAR_DOWN_NETWORK is deferred until state machine enters connected
+                    // state, which will never happen in this case.
+                    onTearDown(TEAR_DOWN_REASON_VCN_REQUESTED);
+                    return;
+                }
+
+                transitionTo(mConnectedState);
+            } else {
+                // Setup data failed.
+                mRetryDelayMillis = response != null ? response.getRetryDurationMillis()
+                        : DataCallResponse.RETRY_DURATION_UNDEFINED;
+                transitionTo(mDisconnectedState);
+            }
+
+            int apnTypeBitmask = ApnSetting.TYPE_NONE;
+            int protocol = ApnSetting.PROTOCOL_UNKNOWN;
+            if (mDataProfile.getApnSetting() != null) {
+                apnTypeBitmask = mDataProfile.getApnSetting().getApnTypeBitmask();
+                protocol = mDataProfile.getApnSetting().getProtocol();
+            }
+            mDataCallSessionStats.onSetupDataCallResponse(response,
+                    getDataNetworkType(),
+                    apnTypeBitmask,
+                    protocol,
+                    // Log the raw fail cause to avoid large amount of UNKNOWN showing on metrics.
+                    response != null ? response.getCause() : mFailCause);
+        }
     }
 
     /**
@@ -1447,11 +1635,13 @@
             sendMessageDelayed(EVENT_STUCK_IN_TRANSIENT_STATE,
                     mDataConfigManager.getNetworkHandoverTimeoutMs());
             notifyPreciseDataConnectionState();
+            updateNetworkScore(true /* keepConnectedForHandover */);
         }
 
         @Override
         public void exit() {
             removeMessages(EVENT_STUCK_IN_TRANSIENT_STATE);
+            updateNetworkScore(false /* keepConnectedForHandover */);
         }
 
         @Override
@@ -2220,54 +2410,6 @@
     }
 
     /**
-     * Setup a data network.
-     */
-    private void setupData() {
-        int dataNetworkType = getDataNetworkType();
-
-        // We need to use the actual modem roaming state instead of the framework roaming state
-        // here. This flag is only passed down to ril_service for picking the correct protocol (for
-        // old modem backward compatibility).
-        boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration();
-
-        // Set this flag to true if the user turns on data roaming. Or if we override the roaming
-        // state in framework, we should set this flag to true as well so the modem will not reject
-        // the data call setup (because the modem actually thinks the device is roaming).
-        boolean allowRoaming = mPhone.getDataRoamingEnabled()
-                || (isModemRoaming && (!mPhone.getServiceState().getDataRoaming()
-                /*|| isUnmeteredUseOnly()*/));
-
-        TrafficDescriptor trafficDescriptor = mDataProfile.getTrafficDescriptor();
-        final boolean matchAllRuleAllowed = trafficDescriptor == null
-                || !TextUtils.isEmpty(trafficDescriptor.getDataNetworkName())
-                // Both OsAppId and APN name are null. This helps for modem to handle when we
-                // are on 5G or LTE with URSP support in falling back to default network.
-                || (TextUtils.isEmpty(trafficDescriptor.getDataNetworkName())
-                && trafficDescriptor.getOsAppId() == null);
-
-        int accessNetwork = DataUtils.networkTypeToAccessNetworkType(dataNetworkType);
-
-        mDataServiceManagers.get(mTransport)
-                .setupDataCall(accessNetwork, mDataProfile, isModemRoaming, allowRoaming,
-                        DataService.REQUEST_REASON_NORMAL, null, mPduSessionId, null,
-                        trafficDescriptor, matchAllRuleAllowed,
-                        obtainMessage(EVENT_SETUP_DATA_NETWORK_RESPONSE));
-
-        int apnTypeBitmask = mDataProfile.getApnSetting() != null
-                ? mDataProfile.getApnSetting().getApnTypeBitmask() : ApnSetting.TYPE_NONE;
-        mDataCallSessionStats.onSetupDataCall(apnTypeBitmask);
-
-        logl("setupData: accessNetwork="
-                + AccessNetworkType.toString(accessNetwork) + ", " + mDataProfile
-                + ", isModemRoaming=" + isModemRoaming + ", allowRoaming=" + allowRoaming
-                + ", PDU session id=" + mPduSessionId + ", matchAllRuleAllowed="
-                + matchAllRuleAllowed);
-        TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(),
-                ServiceState.networkTypeToRilRadioTechnology(dataNetworkType),
-                mDataProfile.getProfileId(), mDataProfile.getApn(), mDataProfile.getProtocolType());
-    }
-
-    /**
      * Get fail cause from {@link DataCallResponse} and the result code.
      *
      * @param resultCode The result code returned from
@@ -2419,6 +2561,8 @@
         mTrafficDescriptors.clear();
         mTrafficDescriptors.addAll(response.getTrafficDescriptors());
 
+        mDefaultQos = response.getDefaultQos();
+
         mQosBearerSessions.clear();
         mQosBearerSessions.addAll(response.getQosBearerSessions());
         if (mQosCallbackTracker != null) {
@@ -2447,93 +2591,13 @@
     }
 
     /**
-     * Called when receiving setup data network response from the data service.
-     *
-     * @param resultCode The result code.
-     * @param response The response.
-     */
-    private void onSetupResponse(@DataServiceCallback.ResultCode int resultCode,
-            @Nullable DataCallResponse response) {
-        logl("onSetupResponse: resultCode=" + DataServiceCallback.resultCodeToString(resultCode)
-                + ", response=" + response);
-        mFailCause = getFailCauseFromDataCallResponse(resultCode, response);
-        validateDataCallResponse(response, true /*isSetupResponse*/);
-        if (mFailCause == DataFailCause.NONE) {
-            DataNetwork dataNetwork = mDataNetworkController.getDataNetworkByInterface(
-                    response.getInterfaceName());
-            if (dataNetwork != null) {
-                logl("Interface " + response.getInterfaceName() + " has been already used by "
-                        + dataNetwork + ". Silently tear down now.");
-                // If this is a pre-5G data setup, that means APN database has some problems. For
-                // example, different APN settings have the same APN name.
-                if (response.getTrafficDescriptors().isEmpty() && dataNetwork.isConnected()) {
-                    reportAnomaly("Duplicate network interface " + response.getInterfaceName()
-                            + " detected.", "62f66e7e-8d71-45de-a57b-dc5c78223fd5");
-                }
-
-                // Do not actually invoke onTearDown, otherwise the existing data network will be
-                // torn down.
-                mRetryDelayMillis = DataCallResponse.RETRY_DURATION_UNDEFINED;
-                mFailCause = DataFailCause.NO_RETRY_FAILURE;
-                transitionTo(mDisconnectedState);
-                return;
-            }
-
-            updateDataNetwork(response);
-
-            // TODO: Evaluate all network requests and see if each request still can be satisfied.
-            //  For requests that can't be satisfied anymore, we need to put them back to the
-            //  unsatisfied pool. If none of network requests can be satisfied, then there is no
-            //  need to mark network agent connected. Just silently deactivate the data network.
-            if (mAttachedNetworkRequestList.size() == 0) {
-                log("Tear down the network since there is no live network request.");
-                // Directly call onTearDown here. Calling tearDown will cause deadlock because
-                // EVENT_TEAR_DOWN_NETWORK is deferred until state machine enters connected state,
-                // which will never happen in this case.
-                onTearDown(TEAR_DOWN_REASON_NO_LIVE_REQUEST);
-                return;
-            }
-
-            if (mVcnManager != null && mVcnManager.applyVcnNetworkPolicy(mNetworkCapabilities,
-                    mLinkProperties).isTeardownRequested()) {
-                log("VCN service requested to tear down the network.");
-                // Directly call onTearDown here. Calling tearDown will cause deadlock because
-                // EVENT_TEAR_DOWN_NETWORK is deferred until state machine enters connected state,
-                // which will never happen in this case.
-                onTearDown(TEAR_DOWN_REASON_VCN_REQUESTED);
-                return;
-            }
-
-            transitionTo(mConnectedState);
-        } else {
-            // Setup data failed.
-            mRetryDelayMillis = response != null ? response.getRetryDurationMillis()
-                    : DataCallResponse.RETRY_DURATION_UNDEFINED;
-            transitionTo(mDisconnectedState);
-        }
-
-        int apnTypeBitmask = ApnSetting.TYPE_NONE;
-        int protocol = ApnSetting.PROTOCOL_UNKNOWN;
-        if (mDataProfile.getApnSetting() != null) {
-            apnTypeBitmask = mDataProfile.getApnSetting().getApnTypeBitmask();
-            protocol = mDataProfile.getApnSetting().getProtocol();
-        }
-        mDataCallSessionStats.onSetupDataCallResponse(response,
-                getDataNetworkType(),
-                apnTypeBitmask,
-                protocol,
-                // Log the raw fail cause to avoid large amount of UNKNOWN showing on metrics.
-                response != null ? response.getCause() : mFailCause);
-    }
-
-    /**
      * If the {@link DataCallResponse} contains invalid info, triggers an anomaly report.
      *
      * @param response The response to be validated
-     * @param isSetupResponse {@code true} if the response is for initial data call setup
+     * @param setupRegState Registration state if the response is for initial data call setup.
      */
     private void validateDataCallResponse(@Nullable DataCallResponse response,
-            boolean isSetupResponse) {
+            @NetworkRegistrationInfo.RegistrationState int setupRegState) {
         if (response == null
                 || response.getLinkStatus() == DataCallResponse.LINK_STATUS_INACTIVE) return;
         int failCause = response.getCause();
@@ -2555,32 +2619,36 @@
             }
             // Check IP for initial setup response
             NetworkRegistrationInfo nri = getNetworkRegistrationInfo();
-            if (isSetupResponse
+            if (setupRegState != -1 // Is setup response
                     && mDataProfile.getApnSetting() != null && nri != null && nri.isInService()) {
+                boolean wasRoaming = setupRegState
+                        == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
                 boolean isRoaming = nri.getNetworkRegistrationState()
                         == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
-                int protocol = isRoaming ? mDataProfile.getApnSetting().getRoamingProtocol()
-                        : mDataProfile.getApnSetting().getProtocol();
-                String underlyingDataService = mTransport
-                        == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
-                        ? "RIL" : "IWLAN data service";
-                if (protocol == ApnSetting.PROTOCOL_IP) {
-                    if (response.getAddresses().stream().anyMatch(
-                            la -> la.getAddress() instanceof java.net.Inet6Address)) {
-                        loge("Invalid DataCallResponse. Requested IPv4 but got IPv6 address. "
-                                + response);
-                        reportAnomaly(underlyingDataService + " reported mismatched IP "
-                                + "type. Requested IPv4 but got IPv6 address.",
-                                "7744f920-fb64-4db0-ba47-de0eae485a80");
-                    }
-                } else if (protocol == ApnSetting.PROTOCOL_IPV6) {
-                    if (response.getAddresses().stream().anyMatch(
-                            la -> la.getAddress() instanceof java.net.Inet4Address)) {
-                        loge("Invalid DataCallResponse. Requested IPv6 but got IPv4 address. "
-                                + response);
-                        reportAnomaly(underlyingDataService + " reported mismatched IP "
-                                        + "type. Requested IPv6 but got IPv4 address.",
-                                "7744f920-fb64-4db0-ba47-de0eae485a80");
+                if (wasRoaming == isRoaming) { // Ignore check if in race condition.
+                    int protocol = isRoaming ? mDataProfile.getApnSetting().getRoamingProtocol()
+                            : mDataProfile.getApnSetting().getProtocol();
+                    String underlyingDataService = mTransport
+                            == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
+                            ? "RIL" : "IWLAN data service";
+                    if (protocol == ApnSetting.PROTOCOL_IP) {
+                        if (response.getAddresses().stream().anyMatch(
+                                la -> la.getAddress() instanceof java.net.Inet6Address)) {
+                            loge("Invalid DataCallResponse. Requested IPv4 but got IPv6 address."
+                                    + response);
+                            reportAnomaly(underlyingDataService + " reported mismatched IP "
+                                            + "type. Requested IPv4 but got IPv6 address.",
+                                    "7744f920-fb64-4db0-ba47-de0eae485a81");
+                        }
+                    } else if (protocol == ApnSetting.PROTOCOL_IPV6) {
+                        if (response.getAddresses().stream().anyMatch(
+                                la -> la.getAddress() instanceof java.net.Inet4Address)) {
+                            loge("Invalid DataCallResponse. Requested IPv6 but got IPv4 address."
+                                    + response);
+                            reportAnomaly(underlyingDataService + " reported mismatched IP "
+                                            + "type. Requested IPv6 but got IPv4 address.",
+                                    "7744f920-fb64-4db0-ba47-de0eae485a81");
+                        }
                     }
                 }
             }
@@ -2717,7 +2785,7 @@
         if (response != null) {
             if (!response.equals(mDataCallResponse)) {
                 log("onDataStateChanged: " + response);
-                validateDataCallResponse(response, false /*isSetupResponse*/);
+                validateDataCallResponse(response, -1 /*setupRegState setup only*/);
                 mDataCallResponse = response;
                 if (response.getLinkStatus() != DataCallResponse.LINK_STATUS_INACTIVE) {
                     updateDataNetwork(response);
@@ -2978,39 +3046,23 @@
         return mLinkStatus;
     }
 
+
     /**
      * Update the network score and report to connectivity service if necessary.
+     *
+     * @param keepConnectedForHandover indicate handover is in progress or not.
      */
-    private void updateNetworkScore() {
-        int networkScore = getNetworkScore();
-        if (networkScore != mNetworkScore) {
-            logl("Updating score from " + mNetworkScore + " to " + networkScore);
-            mNetworkScore = networkScore;
+    private void updateNetworkScore(boolean keepConnectedForHandover) {
+        int connectedReason = keepConnectedForHandover
+                ? NetworkScore.KEEP_CONNECTED_FOR_HANDOVER : NetworkScore.KEEP_CONNECTED_NONE;
+        if (mNetworkScore.getKeepConnectedReason() != connectedReason) {
+            mNetworkScore = new NetworkScore.Builder()
+                    .setKeepConnectedReason(connectedReason).build();
             mNetworkAgent.sendNetworkScore(mNetworkScore);
         }
     }
 
     /**
-     * @return The network score. The higher score of the network has higher chance to be
-     * selected by the connectivity service as active network.
-     */
-    private int getNetworkScore() {
-        // If it's serving a network request that asks NET_CAPABILITY_INTERNET and doesn't have
-        // specify a sub id, this data network is considered to be default internet data
-        // connection. In this case we assign a slightly higher score of 50. The intention is
-        // it will not be replaced by other data networks accidentally in DSDS use case.
-        int score = OTHER_NETWORK_SCORE;
-        for (TelephonyNetworkRequest networkRequest : mAttachedNetworkRequestList) {
-            if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                    && networkRequest.getNetworkSpecifier() == null) {
-                score = DEFAULT_INTERNET_NETWORK_SCORE;
-            }
-        }
-
-        return score;
-    }
-
-    /**
      * @return Network registration info on the current transport.
      */
     private @Nullable NetworkRegistrationInfo getNetworkRegistrationInfo() {
@@ -3154,13 +3206,7 @@
      * @return {@code true} if this data network supports internet.
      */
     public boolean isInternetSupported() {
-        return mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                && mNetworkCapabilities.hasCapability(
-                        NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                && mNetworkCapabilities.hasCapability(
-                        NetworkCapabilities.NET_CAPABILITY_TRUSTED)
-                && mNetworkCapabilities.hasCapability(
-                        NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
+        return mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
     }
 
     /**
@@ -3185,6 +3231,7 @@
                 .setLinkProperties(mLinkProperties)
                 .setNetworkType(getDataNetworkType())
                 .setFailCause(mFailCause)
+                .setDefaultQos(mDefaultQos)
                 .build();
     }
 
@@ -3298,7 +3345,7 @@
         logl("onHandoverResponse: resultCode=" + DataServiceCallback.resultCodeToString(resultCode)
                 + ", response=" + response);
         mFailCause = getFailCauseFromDataCallResponse(resultCode, response);
-        validateDataCallResponse(response, false /*isSetupResponse*/);
+        validateDataCallResponse(response, -1 /*setupRegState setup only*/);
         if (mFailCause == DataFailCause.NONE) {
             // Handover succeeded.
 
@@ -3478,6 +3525,8 @@
                 return "TEAR_DOWN_REASON_ROAMING_DISABLED";
             case TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED:
                 return "TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED";
+            case TEAR_DOWN_REASON_SERVICE_OPTION_NOT_SUPPORTED:
+                return "TEAR_DOWN_REASON_SERVICE_OPTION_NOT_SUPPORTED";
             case TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY:
                 return "TEAR_DOWN_REASON_DATA_SERVICE_NOT_READY";
             case TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER:
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index f9e7510..ab0d326 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -99,6 +99,7 @@
 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
 import com.android.internal.telephony.data.DataStallRecoveryManager.DataStallRecoveryManagerCallback;
 import com.android.internal.telephony.data.LinkBandwidthEstimator.LinkBandwidthEstimatorCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.ims.ImsResolver;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
@@ -112,6 +113,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -296,6 +298,9 @@
      */
     private @DataState int mInternetDataNetworkState = TelephonyManager.DATA_DISCONNECTED;
 
+    /** All the current connected/handover internet networks.  */
+    @NonNull private Set<DataNetwork> mConnectedInternetNetworks = new HashSet<>();
+
     /**
      * The IMS data network state. For now this is just for debugging purposes.
      */
@@ -379,6 +384,8 @@
     /** True after try to release an IMS network; False after try to request an IMS network. */
     private boolean mLastImsOperationIsRelease;
 
+    private final @NonNull FeatureFlags mFeatureFlags;
+
     /** The broadcast receiver. */
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
@@ -559,12 +566,13 @@
                 @ValidationStatus int validationStatus) {}
 
         /**
-         * Called when internet data network is connected.
+         * Called when a network that's capable of internet is newly connected or disconnected.
          *
          * @param internetNetworks The connected internet data network. It should be only one in
          *                         most of the cases.
          */
-        public void onInternetDataNetworkConnected(@NonNull List<DataNetwork> internetNetworks) {}
+        public void onConnectedInternetDataNetworksChanged(@NonNull Set<DataNetwork>
+                internetNetworks) {}
 
         /**
          * Called when data network is connected.
@@ -575,9 +583,6 @@
         public void onDataNetworkConnected(@TransportType int transport,
                 @NonNull DataProfile dataProfile) {}
 
-        /** Called when internet data network is disconnected. */
-        public void onInternetDataNetworkDisconnected() {}
-
         /**
          * Called when any data network existing status changed.
          *
@@ -793,10 +798,13 @@
      * @param phone The phone instance.
      * @param looper The looper to be used by the handler. Currently the handler thread is the
      * phone process's main thread.
+     * @param featureFlags The feature flag.
      */
-    public DataNetworkController(@NonNull Phone phone, @NonNull Looper looper) {
+    public DataNetworkController(@NonNull Phone phone, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags) {
         super(looper);
         mPhone = phone;
+        mFeatureFlags = featureFlags;
         mLogTag = "DNC-" + mPhone.getPhoneId();
         log("DataNetworkController created.");
 
@@ -1290,6 +1298,7 @@
      * {@link #onAttachNetworkRequestsFailed(DataNetwork, NetworkRequestList)} will be invoked.
      */
     private boolean findCompatibleDataNetworkAndAttach(@NonNull NetworkRequestList requestList) {
+        if (requestList.isEmpty()) return false;
         // Try to find a data network that can satisfy all the network requests.
         for (DataNetwork dataNetwork : mDataNetworkList) {
             TelephonyNetworkRequest networkRequest = requestList.stream()
@@ -1307,6 +1316,26 @@
             // When reaching here, it means this data network can satisfy all the network requests.
             logv("Found a compatible data network " + dataNetwork + ". Attaching "
                     + requestList);
+
+            // If WLAN preferred, see whether a more suitable data profile shall be used to satisfy
+            // a short-lived request that doesn't perform handover.
+            int capability = requestList.getFirst().getApnTypeNetworkCapability();
+            int preferredTransport = mAccessNetworksManager
+                    .getPreferredTransportByNetworkCapability(capability);
+            if (capability == NetworkCapabilities.NET_CAPABILITY_MMS
+                    && preferredTransport != dataNetwork.getTransport()
+                    && preferredTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+                DataProfile candidate = mDataProfileManager
+                        .getDataProfileForNetworkRequest(requestList.getFirst(),
+                                TelephonyManager.NETWORK_TYPE_IWLAN,
+                                false/*ignorePermanentFailure*/);
+                if (candidate != null && !dataNetwork.getDataProfile().equals(candidate)) {
+                    logv("But skipped because found better data profile " + candidate
+                            + DataUtils.networkCapabilityToString(capability) + " preferred on "
+                            + AccessNetworkConstants.transportTypeToString(preferredTransport));
+                    continue;
+                }
+            }
             return dataNetwork.attachNetworkRequests(requestList);
         }
         return false;
@@ -1548,6 +1577,11 @@
             evaluation.addDataDisallowedReason(DataDisallowedReason.CDMA_EMERGENCY_CALLBACK_MODE);
         }
 
+        // Check whether data is disallowed while using satellite
+        if (isDataDisallowedDueToSatellite(networkRequest.getCapabilities())) {
+            evaluation.addDataDisallowedReason(DataDisallowedReason.SERVICE_OPTION_NOT_SUPPORTED);
+        }
+
         // Check if only one data network is allowed.
         if (isOnlySingleDataNetworkAllowed(transport)
                 && !hasCapabilityExemptsFromSinglePdnRule(networkRequest.getCapabilities())) {
@@ -1725,6 +1759,12 @@
             evaluation.addDataDisallowedReason(DataDisallowedReason.CDMA_EMERGENCY_CALLBACK_MODE);
         }
 
+        // Check whether data is disallowed while using satellite
+        if (isDataDisallowedDueToSatellite(dataNetwork.getNetworkCapabilities()
+                .getCapabilities())) {
+            evaluation.addDataDisallowedReason(DataDisallowedReason.SERVICE_OPTION_NOT_SUPPORTED);
+        }
+
         // Check if there are other network that has higher priority, and only single data network
         // is allowed.
         if (isOnlySingleDataNetworkAllowed(dataNetwork.getTransport())
@@ -2067,6 +2107,8 @@
                     return DataNetwork.TEAR_DOWN_REASON_SIM_REMOVAL;
                 case CONCURRENT_VOICE_DATA_NOT_ALLOWED:
                     return DataNetwork.TEAR_DOWN_REASON_CONCURRENT_VOICE_DATA_NOT_ALLOWED;
+                case SERVICE_OPTION_NOT_SUPPORTED:
+                    return DataNetwork.TEAR_DOWN_REASON_SERVICE_OPTION_NOT_SUPPORTED;
                 case RADIO_POWER_OFF:
                     return DataNetwork.TEAR_DOWN_REASON_AIRPLANE_MODE_ON;
                 case PENDING_TEAR_DOWN_ALL:
@@ -2113,8 +2155,7 @@
         for (DataNetwork dataNetwork : mDataNetworkList) {
             if (dataNetwork.getId() == cid
                     && dataNetwork.isConnected()
-                    && dataNetwork.getNetworkCapabilities()
-                    .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+                    && dataNetwork.isInternetSupported()) {
                 return true;
             }
         }
@@ -2704,7 +2745,7 @@
             mPreviousConnectedDataNetworkList.remove(MAX_HISTORICAL_CONNECTED_DATA_NETWORKS);
         }
 
-        updateOverallInternetDataState();
+        if (dataNetwork.isInternetSupported()) updateOverallInternetDataState();
 
         if (dataNetwork.getNetworkCapabilities().hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_IMS)) {
@@ -2896,7 +2937,7 @@
      */
     private void onDataNetworkSuspendedStateChanged(@NonNull DataNetwork dataNetwork,
             boolean suspended) {
-        updateOverallInternetDataState();
+        if (dataNetwork.isInternetSupported()) updateOverallInternetDataState();
 
         if (dataNetwork.getNetworkCapabilities().hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_IMS)) {
@@ -2923,7 +2964,7 @@
         mDataNetworkList.remove(dataNetwork);
         mPendingImsDeregDataNetworks.remove(dataNetwork);
         mDataRetryManager.cancelPendingHandoverRetry(dataNetwork);
-        updateOverallInternetDataState();
+        if (dataNetwork.isInternetSupported()) updateOverallInternetDataState();
 
         if (dataNetwork.getNetworkCapabilities().hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_IMS)) {
@@ -3195,16 +3236,18 @@
             logl("Start handover " + dataNetwork + " to "
                     + AccessNetworkConstants.transportTypeToString(targetTransport));
             dataNetwork.startHandover(targetTransport, dataHandoverRetryEntry);
-        } else if (dataEvaluation.containsOnly(DataDisallowedReason.NOT_IN_SERVICE)
-                && dataNetwork.shouldDelayImsTearDownDueToInCall()) {
-            // We try to preserve voice call in the case of temporary preferred transport mismatch
+        } else if (dataNetwork.shouldDelayImsTearDownDueToInCall()
+                && dataEvaluation.containsOnly(
+                        DataDisallowedReason.NOT_IN_SERVICE,
+                        DataDisallowedReason.NOT_ALLOWED_BY_POLICY)) {
+            // We try our best to preserve the voice call by retrying later
             if (dataHandoverRetryEntry != null) {
                 dataHandoverRetryEntry.setState(DataRetryEntry.RETRY_STATE_FAILED);
             }
             mDataRetryManager.evaluateDataHandoverRetry(dataNetwork,
                     DataFailCause.HANDOVER_FAILED,
                     DataCallResponse.RETRY_DURATION_UNDEFINED /* retry mills */);
-            logl("tryHandoverDataNetwork: Scheduled retry due to in voice call and target OOS");
+            logl("tryHandoverDataNetwork: Scheduled retry due to in voice call");
         } else if (dataEvaluation.containsAny(DataDisallowedReason.NOT_ALLOWED_BY_POLICY,
                 DataDisallowedReason.NOT_IN_SERVICE,
                 DataDisallowedReason.VOPS_NOT_SUPPORTED)) {
@@ -3213,8 +3256,7 @@
                     + AccessNetworkConstants.transportTypeToString(targetTransport));
             tearDownGracefully(dataNetwork,
                     DataNetwork.TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED);
-        } else if (dataEvaluation.containsAny(DataDisallowedReason.ILLEGAL_STATE,
-                DataDisallowedReason.RETRY_SCHEDULED)) {
+        } else if (dataEvaluation.containsAny(DataDisallowedReason.ILLEGAL_STATE)) {
             logl("tryHandoverDataNetwork: Handover not allowed. " + dataNetwork
                     + " will remain on " + AccessNetworkConstants.transportTypeToString(
                     dataNetwork.getTransport()));
@@ -3319,8 +3361,7 @@
             dataNetwork.attachNetworkRequests(networkRequestList);
         }
 
-        if (dataNetwork.getNetworkCapabilities().hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+        if (dataNetwork.isInternetSupported()) {
             // Update because DataNetwork#isInternetSupported might have changed with capabilities.
             updateOverallInternetDataState();
         }
@@ -3347,6 +3388,10 @@
             return true;
         }
 
+        if (!oldNri.isNonTerrestrialNetwork() && newNri.isNonTerrestrialNetwork()) {
+            return true;
+        }
+
         DataSpecificRegistrationInfo oldDsri = oldNri.getDataSpecificInfo();
         DataSpecificRegistrationInfo newDsri = newNri.getDataSpecificInfo();
 
@@ -3399,6 +3444,10 @@
             return true;
         }
 
+        if (oldSS.isUsingNonTerrestrialNetwork() && !newSS.isUsingNonTerrestrialNetwork()) {
+            return true;
+        }
+
         DataSpecificRegistrationInfo oldDsri = oldPsNri.getDataSpecificInfo();
         DataSpecificRegistrationInfo newDsri = newPsNri.getDataSpecificInfo();
 
@@ -3447,7 +3496,12 @@
                                 oldNri.getRegistrationState()) : null);
                 debugMessage.append("->").append(newNri != null
                         ? NetworkRegistrationInfo.registrationStateToString(
-                        newNri.getRegistrationState()) : null).append("] ");
+                        newNri.getRegistrationState()) : null).append(", ");
+                debugMessage.append(oldNri != null ? NetworkRegistrationInfo
+                        .isNonTerrestrialNetworkToString(oldNri.isNonTerrestrialNetwork()) : null);
+                debugMessage.append("->").append(newNri != null ? NetworkRegistrationInfo
+                        .isNonTerrestrialNetworkToString(newNri.isNonTerrestrialNetwork()) : null)
+                        .append("] ");
                 if (shouldReevaluateDataNetworks(oldNri, newNri)) {
                     if (!hasMessages(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)) {
                         sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
@@ -3484,11 +3538,11 @@
                 .anyMatch(dataNetwork -> dataNetwork.isInternetSupported()
                         && (dataNetwork.isConnected() || dataNetwork.isHandoverInProgress()));
         // If any one is not suspended, then the overall is not suspended.
-        List<DataNetwork> allConnectedInternetDataNetworks = mDataNetworkList.stream()
+        Set<DataNetwork> allConnectedInternetDataNetworks = mDataNetworkList.stream()
                 .filter(DataNetwork::isInternetSupported)
                 .filter(dataNetwork -> dataNetwork.isConnected()
                         || dataNetwork.isHandoverInProgress())
-                .collect(Collectors.toList());
+                .collect(Collectors.toSet());
         boolean isSuspended = !allConnectedInternetDataNetworks.isEmpty()
                 && allConnectedInternetDataNetworks.stream().allMatch(DataNetwork::isSuspended);
         logv("isSuspended=" + isSuspended + ", anyInternetConnected=" + anyInternetConnected
@@ -3505,19 +3559,15 @@
             logl("Internet data state changed from "
                     + TelephonyUtils.dataStateToString(mInternetDataNetworkState) + " to "
                     + TelephonyUtils.dataStateToString(dataNetworkState) + ".");
-            // TODO: Create a new route to notify TelephonyRegistry.
-            if (dataNetworkState == TelephonyManager.DATA_CONNECTED
-                    && mInternetDataNetworkState == TelephonyManager.DATA_DISCONNECTED) {
-                mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
-                        () -> callback.onInternetDataNetworkConnected(
-                                allConnectedInternetDataNetworks)));
-            } else if (dataNetworkState == TelephonyManager.DATA_DISCONNECTED
-                    && mInternetDataNetworkState == TelephonyManager.DATA_CONNECTED) {
-                mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
-                        callback::onInternetDataNetworkDisconnected));
-            } // TODO: Add suspended callback if needed.
             mInternetDataNetworkState = dataNetworkState;
         }
+        // Check data network reference equality to update current connected internet networks.
+        if (!mConnectedInternetNetworks.equals(allConnectedInternetDataNetworks)) {
+            mConnectedInternetNetworks = allConnectedInternetDataNetworks;
+            mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
+                    () -> callback.onConnectedInternetDataNetworksChanged(
+                            allConnectedInternetDataNetworks)));
+        }
     }
 
     /**
@@ -3765,6 +3815,41 @@
     }
 
     /**
+     * Check whether data is disallowed while using satellite
+     * @param capabilities An array of the NetworkCapabilities to be checked
+     * @return {@code true} if the capabilities contain any capability that are restricted
+     * while using satellite else {@code false}
+     */
+    private boolean isDataDisallowedDueToSatellite(@NetCapability int[] capabilities) {
+        if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            return false;
+        }
+
+        if (!mServiceState.isUsingNonTerrestrialNetwork()) {
+            // Device is not connected to satellite
+            return false;
+        }
+
+        Set<Integer> restrictedCapabilities = Set.of(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        if (Arrays.stream(capabilities).noneMatch(restrictedCapabilities::contains)) {
+            // Only internet data disallowed while using satellite
+            return false;
+        }
+
+        for (NetworkRegistrationInfo nri : mServiceState.getNetworkRegistrationInfoList()) {
+            if (nri.isNonTerrestrialNetwork()
+                    && nri.getAvailableServices().contains(
+                            NetworkRegistrationInfo.SERVICE_TYPE_DATA)) {
+                // Data is supported while using satellite
+                return false;
+            }
+        }
+
+        // Data is disallowed while using satellite
+        return true;
+    }
+
+    /**
      * Log debug messages.
      * @param s debug messages
      */
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index 0878ccf..23029e3 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -163,8 +163,9 @@
         mDataNetworkController.registerDataNetworkControllerCallback(
                 new DataNetworkControllerCallback(this::post) {
                     @Override
-                    public void onInternetDataNetworkConnected(
-                            @NonNull List<DataNetwork> internetNetworks) {
+                    public void onConnectedInternetDataNetworksChanged(
+                            @NonNull Set<DataNetwork> internetNetworks) {
+                        if (internetNetworks.isEmpty()) return;
                         DataProfileManager.this.onInternetDataNetworkConnected(internetNetworks);
                     }
 
@@ -405,28 +406,23 @@
     }
 
     /**
-     * Called when internet data is connected.
+     * Called when new internet data connect.
      *
      * @param internetNetworks The connected internet data networks.
      */
-    private void onInternetDataNetworkConnected(@NonNull List<DataNetwork> internetNetworks) {
-        DataProfile defaultProfile = null;
-        if (internetNetworks.size() == 1) {
-            // Most of the cases there should be only one.
-            defaultProfile = internetNetworks.get(0).getDataProfile();
-        } else if (internetNetworks.size() > 1) {
-            // but in case there are multiple, find the default internet network, and choose the
-            // one which has longest life cycle.
-            logv("onInternetDataNetworkConnected: mPreferredDataProfile=" + mPreferredDataProfile
-                    + " internetNetworks=" + internetNetworks);
-            defaultProfile = internetNetworks.stream()
-                    .filter(network -> mPreferredDataProfile == null
-                            || canPreferredDataProfileSatisfy(
-                            network.getAttachedNetworkRequestList()))
-                    .map(DataNetwork::getDataProfile)
-                    .min(Comparator.comparingLong(DataProfile::getLastSetupTimestamp))
-                    .orElse(null);
-        }
+    private void onInternetDataNetworkConnected(@NonNull Set<DataNetwork> internetNetworks) {
+        // Most of the cases there should be only one.
+        // but in case there are multiple, find the default internet network, and choose the
+        // one which has longest life cycle.
+        DataProfile defaultProfile = internetNetworks.stream()
+                .filter(network -> mPreferredDataProfile == null
+                        // Find the one most resembles the current preferred profile,
+                        // avoiding e.g. DUN default network.
+                        || canPreferredDataProfileSatisfy(
+                        network.getAttachedNetworkRequestList()))
+                .map(DataNetwork::getDataProfile)
+                .min(Comparator.comparingLong(DataProfile::getLastSetupTimestamp))
+                .orElse(null);
 
         // Update a working internet data profile as a future candidate for preferred data profile
         // after APNs are reset to default
@@ -436,6 +432,9 @@
         // brought up a network means it passed sophisticated checks, update the preferred data
         // profile so that this network won't be torn down in future network evaluations.
         if (defaultProfile == null || defaultProfile.equals(mPreferredDataProfile)) return;
+        logv("onInternetDataNetworkConnected: defaultProfile=" + defaultProfile
+                + " previous preferredDataProfile=" + mPreferredDataProfile
+                + " internetNetworks=" + internetNetworks);
         // Save the preferred data profile into database.
         setPreferredDataProfile(defaultProfile);
         updateDataProfiles(false/*force update IA*/);
@@ -979,6 +978,7 @@
         // The following fields in apn1 and apn2 should be the same, otherwise ApnSetting.similar()
         // should fail earlier.
         apnBuilder.setApnName(apn1.getApnName());
+        apnBuilder.setOperatorNumeric(apn1.getOperatorNumeric());
         apnBuilder.setProtocol(apn1.getProtocol());
         apnBuilder.setRoamingProtocol(apn1.getRoamingProtocol());
         apnBuilder.setCarrierEnabled(apn1.isEnabled());
diff --git a/src/java/com/android/internal/telephony/data/DataRetryManager.java b/src/java/com/android/internal/telephony/data/DataRetryManager.java
index 3146689..4465fbb 100644
--- a/src/java/com/android/internal/telephony/data/DataRetryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataRetryManager.java
@@ -22,11 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.NetworkCapabilities;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -78,11 +73,6 @@
 public class DataRetryManager extends Handler {
     private static final boolean VDBG = false;
 
-    /** Intent of Alarm Manager for long retry timer. */
-    private static final String ACTION_RETRY = "com.android.internal.telephony.data.ACTION_RETRY";
-    /** The extra key for the hashcode of the retry entry for Alarm Manager. */
-    private static final String ACTION_RETRY_EXTRA_HASHCODE = "extra_retry_hashcode";
-
     /** Event for data setup retry. */
     private static final int EVENT_DATA_SETUP_RETRY = 3;
 
@@ -1018,19 +1008,6 @@
         mRil.registerForOn(this, EVENT_RADIO_ON, null);
         mRil.registerForModemReset(this, EVENT_MODEM_RESET, null);
 
-        // Register intent of alarm manager for long retry timer
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(ACTION_RETRY);
-        mPhone.getContext().registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (ACTION_RETRY.equals(intent.getAction())) {
-                    DataRetryManager.this.onAlarmIntentRetry(
-                            intent.getIntExtra(ACTION_RETRY_EXTRA_HASHCODE, -1 /*Bad hashcode*/));
-                }
-            }
-        }, intentFilter);
-
         if (mDataConfigManager.shouldResetDataThrottlingWhenTacChanges()) {
             mPhone.getServiceStateTracker().registerForAreaCodeChanged(this, EVENT_TAC_CHANGED,
                     null);
@@ -1450,8 +1427,7 @@
      * @param dataRetryEntry The data retry entry.
      */
     private void schedule(@NonNull DataRetryEntry dataRetryEntry) {
-        logl("Scheduled data retry " + dataRetryEntry
-                + " hashcode=" + dataRetryEntry.hashCode());
+        logl("Scheduled data retry " + dataRetryEntry + " hashcode=" + dataRetryEntry.hashCode());
         mDataRetryEntries.add(dataRetryEntry);
         if (mDataRetryEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) {
             // Discard the oldest retry entry.
@@ -1467,32 +1443,19 @@
                             ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry),
                     dataRetryEntry.retryDelayMillis);
         } else {
-            Intent intent = new Intent(ACTION_RETRY);
-            intent.putExtra(ACTION_RETRY_EXTRA_HASHCODE, dataRetryEntry.hashCode());
             // No need to wake up the device, the retry can wait util next time the device wake up
             // to save power.
             mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,
                     dataRetryEntry.retryElapsedTime,
-                    PendingIntent.getBroadcast(mPhone.getContext(),
-                            dataRetryEntry.hashCode() /*Unique identifier of this retry attempt*/,
-                            intent,
-                            PendingIntent.FLAG_IMMUTABLE));
-        }
-    }
-
-    /**
-     * Called when it's time to retry scheduled by Alarm Manager.
-     * @param retryHashcode The hashcode is the unique identifier of which retry entry to retry.
-     */
-    private void onAlarmIntentRetry(int retryHashcode) {
-        DataRetryEntry dataRetryEntry = mDataRetryEntries.stream()
-                .filter(entry -> entry.hashCode() == retryHashcode)
-                .findAny()
-                .orElse(null);
-        logl("onAlarmIntentRetry: found " + dataRetryEntry + " with hashcode " + retryHashcode);
-        if (dataRetryEntry != null) {
-            sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry
-                    ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry));
+                    "dataRetryHash-" + dataRetryEntry.hashCode() /*debug tag*/,
+                    Runnable::run,
+                    null /*worksource*/,
+                    () -> {
+                        logl("onAlarm retry " + dataRetryEntry);
+                        sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry
+                                ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY,
+                                dataRetryEntry));
+                    });
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
index f8365bb..e54f6d3 100644
--- a/src/java/com/android/internal/telephony/data/DataSettingsManager.java
+++ b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
@@ -28,6 +28,7 @@
 import android.provider.Settings;
 import android.sysprop.TelephonyProperties;
 import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyManager.MobileDataPolicy;
@@ -208,7 +209,9 @@
             case EVENT_SUBSCRIPTIONS_CHANGED: {
                 mSubId = (int) msg.obj;
                 refreshEnabledMobileDataPolicy();
-                updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_USER);
+                updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_USER,
+                        mPhone.getContext().getOpPackageName(),
+                        SubscriptionManager.isValidSubscriptionId(mSubId));
                 mPhone.notifyUserMobileDataStateChanged(isUserDataEnabled());
                 break;
             }
@@ -342,15 +345,15 @@
     }
 
     private void updateDataEnabledAndNotify(@TelephonyManager.DataEnabledChangedReason int reason) {
-        updateDataEnabledAndNotify(reason, mPhone.getContext().getOpPackageName());
+        updateDataEnabledAndNotify(reason, mPhone.getContext().getOpPackageName(), false);
     }
 
     private void updateDataEnabledAndNotify(@TelephonyManager.DataEnabledChangedReason int reason,
-            @NonNull String callingPackage) {
+            @NonNull String callingPackage, boolean shouldNotify) {
         boolean prevDataEnabled = mIsDataEnabled;
         mIsDataEnabled = isDataEnabled(ApnSetting.TYPE_ALL);
         log("mIsDataEnabled=" + mIsDataEnabled + ", prevDataEnabled=" + prevDataEnabled);
-        if (!mInitialized || prevDataEnabled != mIsDataEnabled) {
+        if (!mInitialized || shouldNotify || prevDataEnabled != mIsDataEnabled) {
             if (!mInitialized) mInitialized = true;
             notifyDataEnabledChanged(mIsDataEnabled, reason, callingPackage);
         }
@@ -443,7 +446,8 @@
             mPhone.notifyUserMobileDataStateChanged(enabled);
             mDataSettingsManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                     () -> callback.onUserDataEnabledChanged(enabled, callingPackage)));
-            updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_USER, callingPackage);
+            updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_USER,
+                    callingPackage, false);
         }
     }
 
@@ -474,7 +478,8 @@
         if (mDataEnabledSettings.get(TelephonyManager.DATA_ENABLED_REASON_POLICY) != enabled) {
             logl("PolicyDataEnabled changed to " + enabled + ", callingPackage=" + callingPackage);
             mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_POLICY, enabled);
-            updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_POLICY, callingPackage);
+            updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_POLICY,
+                    callingPackage, false);
         }
     }
 
@@ -488,7 +493,7 @@
             logl("CarrierDataEnabled changed to " + enabled + ", callingPackage=" + callingPackage);
             mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled);
             updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_CARRIER,
-                    callingPackage);
+                    callingPackage, false);
         }
     }
 
@@ -502,7 +507,7 @@
             logl("ThermalDataEnabled changed to " + enabled + ", callingPackage=" + callingPackage);
             mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_THERMAL, enabled);
             updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_THERMAL,
-                    callingPackage);
+                    callingPackage, false);
         }
     }
 
@@ -750,23 +755,22 @@
         boolean isNonDds = mPhone.getSubId() != SubscriptionManagerService.getInstance()
                 .getDefaultDataSubId();
 
+        Phone defaultDataPhone = PhoneFactory.getPhone(SubscriptionManagerService.getInstance()
+                .getPhoneId(SubscriptionManagerService.getInstance()
+                        .getDefaultDataSubId()));
+        boolean isDdsUserEnabled = defaultDataPhone != null && defaultDataPhone.isUserDataEnabled();
+
         // mobile data policy : data during call
         if (isMobileDataPolicyEnabled(TelephonyManager
                 .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL)) {
-            overridden = overridden || isNonDds && mPhone.getState() != PhoneConstants.State.IDLE;
+            overridden |= isNonDds && isDdsUserEnabled
+                    && mPhone.getState() != PhoneConstants.State.IDLE;
         }
 
         // mobile data policy : auto data switch
         if (isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)) {
             // check user enabled data on the default data phone
-            Phone defaultDataPhone = PhoneFactory.getPhone(SubscriptionManagerService.getInstance()
-                    .getPhoneId(SubscriptionManagerService.getInstance()
-                            .getDefaultDataSubId()));
-            if (defaultDataPhone == null) {
-                loge("isDataEnabledOverriddenForApn: unexpected defaultDataPhone is null");
-            } else {
-                overridden = overridden || isNonDds && defaultDataPhone.isUserDataEnabled();
-            }
+            overridden |= isNonDds && isDdsUserEnabled;
         }
         return overridden;
     }
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
index 375b250..f2b6776 100644
--- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -25,6 +25,7 @@
 import android.content.Intent;
 import android.database.ContentObserver;
 import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -56,7 +57,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
-import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -191,7 +192,7 @@
     private boolean mMobileDataChangedToEnabledDuringDataStall;
     /** Whether attempted all recovery steps. */
     private boolean mIsAttemptedAllSteps;
-    /** Whether internet network connected. */
+    /** Whether internet network that require validation is connected. */
     private boolean mIsInternetNetworkConnected;
     /** The durations for current recovery action */
     private @ElapsedRealtimeLong long mTimeElapsedOfCurrentAction;
@@ -307,16 +308,26 @@
                     }
 
                     @Override
-                    public void onInternetDataNetworkConnected(
-                            @NonNull List<DataNetwork> internetNetworks) {
-                        mIsInternetNetworkConnected = true;
-                        logl("onInternetDataNetworkConnected");
-                    }
-
-                    @Override
-                    public void onInternetDataNetworkDisconnected() {
-                        mIsInternetNetworkConnected = false;
-                        logl("onInternetDataNetworkDisconnected");
+                    public void onConnectedInternetDataNetworksChanged(
+                            @NonNull Set<DataNetwork> internetNetworks) {
+                        boolean anyInternetRequireValidatedConnected = internetNetworks.stream()
+                                .anyMatch(nw -> {
+                                    NetworkCapabilities capabilities = nw.getNetworkCapabilities();
+                                    // Only track the networks that require validation.
+                                    // The criteria is base on NetworkMonitorUtils.java.
+                                    return capabilities.hasCapability(
+                                            NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                                            && capabilities.hasCapability(
+                                            NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+                                            && capabilities.hasCapability(
+                                            NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
+                                });
+                        if (mIsInternetNetworkConnected != anyInternetRequireValidatedConnected) {
+                            mIsInternetNetworkConnected = anyInternetRequireValidatedConnected;
+                            logl(mIsInternetNetworkConnected
+                                    ? "At Least One InternetDataNetwork Connected"
+                                    : "All InternetDataNetwork Disconnected");
+                        }
                     }
                 });
         mPhone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index a5b3da2..ab759e9 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -194,8 +194,6 @@
     private int mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
     /** The reason for the last time changing preferred data sub **/
     private int mLastSwitchPreferredDataReason = -1;
-    /** {@code true} if we've displayed the notification the first time auto switch occurs **/
-    private boolean mDisplayedAutoSwitchNotification = false;
     private boolean mPendingSwitchNeedValidation;
     @VisibleForTesting
     public final CellularNetworkValidator.ValidationCallback mValidationCallback =
@@ -312,15 +310,17 @@
     // Default timeout value of network validation in millisecond.
     private final static int DEFAULT_VALIDATION_EXPIRATION_TIME = 2000;
 
+    /** Controller that tracks {@link TelephonyManager#MOBILE_DATA_POLICY_AUTO_DATA_SWITCH} */
+    @NonNull private final AutoDataSwitchController mAutoDataSwitchController;
+    /** Callback to deal with requests made by the auto data switch controller. */
+    @NonNull private final AutoDataSwitchController.AutoDataSwitchControllerCallback
+            mAutoDataSwitchCallback;
+
     private ConnectivityManager mConnectivityManager;
     private int mImsRegistrationTech = REGISTRATION_TECH_NONE;
 
     private List<Set<CommandException.Error>> mCurrentDdsSwitchFailure;
 
-    private AutoDataSwitchController mAutoDataSwitchController;
-    private AutoDataSwitchController.AutoDataSwitchControllerCallback
-            mAutoDataSwitchCallback;
-
     /** Data settings manager callback. Key is the phone id. */
     private final @NonNull Map<Integer, DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
             new ArrayMap<>();
@@ -513,7 +513,7 @@
         TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
                 context.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
         telephonyRegistryManager.addOnSubscriptionsChangedListener(
-                mSubscriptionsChangedListener, mSubscriptionsChangedListener.getHandlerExecutor());
+                mSubscriptionsChangedListener, this::post);
 
         mConnectivityManager =
             (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -647,6 +647,7 @@
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case EVENT_SUBSCRIPTION_CHANGED: {
+                mAutoDataSwitchController.notifySubscriptionsChanged();
                 onEvaluate(REQUESTS_UNCHANGED, "subscription changed");
                 break;
             }
@@ -1387,7 +1388,7 @@
         return defaultDataPhone != null // check user enabled data
                 && defaultDataPhone.isUserDataEnabled()
                 && voicePhone != null // check user enabled voice during call feature
-                && voicePhone.isDataAllowed();
+                && voicePhone.getDataSettingsManager().isDataEnabled();
     }
 
     protected void transitionToEmergencyPhone() {
@@ -1838,7 +1839,6 @@
         pw.println("mCurrentDdsSwitchFailure=" + mCurrentDdsSwitchFailure);
         pw.println("mLastSwitchPreferredDataReason="
                 + switchReasonToString(mLastSwitchPreferredDataReason));
-        pw.println("mDisplayedAutoSwitchNotification=" + mDisplayedAutoSwitchNotification);
         pw.println("Local logs:");
         pw.increaseIndent();
         mLocalLog.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
index cbb74fa..65ac8b3 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
@@ -150,9 +150,14 @@
             throw new IllegalStateException("DomainSelection is not supported!");
         }
 
-        if (phone == null || !phone.isImsAvailable()) {
-            // If ImsPhone is null or the binder of ImsService is not available,
-            // CS domain is used for the telephony services.
+        if (phone == null || phone.getImsPhone() == null
+                || (!(isEmergency && selectorType == DomainSelectionService.SELECTOR_TYPE_CALLING)
+                        && !phone.isImsAvailable())) {
+            // In case of emergency calls, to recover the temporary failure in IMS service
+            // connection, DomainSelection shall be started even when IMS isn't available.
+            // DomainSelector will keep finding next available transport.
+            // For other telephony services, if the binder of ImsService is not available,
+            // CS domain will be used.
             return null;
         }
 
diff --git a/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
index e157d24..516d6b9 100644
--- a/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
@@ -38,15 +38,6 @@
 
     private static final boolean DBG = false;
 
-    private static final String PREFIX_WPS = "*272";
-
-    // WPS prefix when CLIR is being activated for the call.
-    private static final String PREFIX_WPS_CLIR_ACTIVATE = "*31#*272";
-
-    // WPS prefix when CLIR is being deactivated for the call.
-    private static final String PREFIX_WPS_CLIR_DEACTIVATE = "#31#*272";
-
-
     private @Nullable DomainSelectionConnectionCallback mCallback;
 
     /**
@@ -132,15 +123,4 @@
         }
         return builder.build();
     }
-
-    /**
-     * Check if the call is Wireless Priority Service call
-     * @param dialString  The number being dialed.
-     * @return {@code true} if dialString matches WPS pattern and {@code false} otherwise.
-     */
-    public static boolean isWpsCall(String dialString) {
-        return (dialString != null) && (dialString.startsWith(PREFIX_WPS)
-                || dialString.startsWith(PREFIX_WPS_CLIR_ACTIVATE)
-                || dialString.startsWith(PREFIX_WPS_CLIR_DEACTIVATE));
-    }
 }
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index 96cd880..a45e956 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.emergency;
 
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL;
+
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK;
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_NONE;
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
@@ -26,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
@@ -33,6 +36,7 @@
 import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.UserHandle;
+import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.sysprop.TelephonyProperties;
 import android.telephony.Annotation.DisconnectCauses;
@@ -94,6 +98,8 @@
     /** Indicates the emergency type is SMS. */
     public static final int EMERGENCY_TYPE_SMS = 2;
 
+    private static final String KEY_NO_SIM_ECBM_SUPPORT = "no_sim_ecbm_support";
+
     private static EmergencyStateTracker INSTANCE = null;
 
     private final Context mContext;
@@ -132,6 +138,12 @@
     private CompletableFuture<Integer> mSmsEmergencyModeFuture;
     private boolean mIsTestEmergencyNumberForSms;
 
+    private final android.util.ArrayMap<Integer, Boolean> mNoSimEcbmSupported =
+            new android.util.ArrayMap<>();
+    private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+            (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged(
+                    slotIndex, subId);
+
     /**
      * Listens for Emergency Callback Mode state change intents
      */
@@ -356,6 +368,13 @@
         mWakeLock = (pm != null) ? pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                 "telephony:" + TAG) : null;
         mConfigManager = context.getSystemService(CarrierConfigManager.class);
+        if (mConfigManager != null) {
+            // Carrier config changed callback should be executed in handler thread
+            mConfigManager.registerCarrierConfigChangeListener(mHandler::post,
+                    mCarrierConfigChangeListener);
+        } else {
+            Rlog.e(TAG, "CarrierConfigLoader is not available.");
+        }
 
         // Register receiver for ECM exit.
         IntentFilter filter = new IntentFilter();
@@ -392,6 +411,8 @@
         mEcmExitTimeoutMs = ecmExitTimeoutMs;
         mWakeLock = null; // Don't declare a wakelock in tests
         mConfigManager = context.getSystemService(CarrierConfigManager.class);
+        mConfigManager.registerCarrierConfigChangeListener(mHandler::post,
+                mCarrierConfigChangeListener);
         IntentFilter filter = new IntentFilter();
         filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
         context.registerReceiver(mEcmExitReceiver, filter, null, mHandler);
@@ -743,10 +764,32 @@
     /**
      * Returns {@code true} if device and carrier support emergency callback mode.
      */
-    private boolean isEmergencyCallbackModeSupported() {
-        return getConfig(mPhone.getSubId(),
-                CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL,
-                DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED);
+    @VisibleForTesting
+    public boolean isEmergencyCallbackModeSupported() {
+        int subId = mPhone.getSubId();
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            // If there is no SIM, refer to the saved last carrier configuration with valid
+            // subscription.
+            int phoneId = mPhone.getPhoneId();
+            Boolean savedConfig = mNoSimEcbmSupported.get(Integer.valueOf(phoneId));
+            if (savedConfig == null) {
+                // Exceptional case such as with poor boot performance.
+                // Usually, the first carrier config change will update the cache.
+                // But with poor boot performance, the carrier config change
+                // can be delayed for a long time.
+                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+                savedConfig = Boolean.valueOf(
+                        sp.getBoolean(KEY_NO_SIM_ECBM_SUPPORT + phoneId, false));
+                Rlog.i(TAG, "ECBM value not cached, load from preference");
+                mNoSimEcbmSupported.put(Integer.valueOf(phoneId), savedConfig);
+            }
+            Rlog.i(TAG, "isEmergencyCallbackModeSupported savedConfig=" + savedConfig);
+            return savedConfig;
+        } else {
+            return getConfig(subId,
+                    CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL,
+                    DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED);
+        }
     }
 
     /**
@@ -1205,4 +1248,60 @@
             default: return "UNKNOWN";
         }
     }
+
+    private void onCarrierConfigurationChanged(int slotIndex, int subId) {
+        Rlog.i(TAG, "onCarrierConfigChanged slotIndex=" + slotIndex + ", subId=" + subId);
+
+        if (slotIndex < 0) {
+            return;
+        }
+
+        updateNoSimEcbmSupported(slotIndex, subId);
+    }
+
+    private void updateNoSimEcbmSupported(int slotIndex, int subId) {
+        SharedPreferences sp = null;
+        Boolean savedConfig = mNoSimEcbmSupported.get(Integer.valueOf(slotIndex));
+        if (savedConfig == null) {
+            sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+            savedConfig = Boolean.valueOf(
+                    sp.getBoolean(KEY_NO_SIM_ECBM_SUPPORT + slotIndex, false));
+            mNoSimEcbmSupported.put(Integer.valueOf(slotIndex), savedConfig);
+            Rlog.i(TAG, "updateNoSimEcbmSupported load from preference slotIndex=" + slotIndex
+                    + ", supported=" + savedConfig);
+        }
+
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            // invalid subId
+            return;
+        }
+
+        PersistableBundle b = getConfigBundle(subId, KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL);
+        if (b.isEmpty()) {
+            Rlog.e(TAG, "updateNoSimEcbmSupported empty result");
+            return;
+        }
+
+        if (!CarrierConfigManager.isConfigForIdentifiedCarrier(b)) {
+            Rlog.i(TAG, "updateNoSimEcbmSupported not carrier specific configuration");
+            return;
+        }
+
+        boolean carrierConfig = b.getBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL);
+        if (carrierConfig == savedConfig) {
+            return;
+        }
+
+        mNoSimEcbmSupported.put(Integer.valueOf(slotIndex), Boolean.valueOf(carrierConfig));
+
+        if (sp == null) {
+            sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        }
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putBoolean(KEY_NO_SIM_ECBM_SUPPORT + slotIndex, carrierConfig);
+        editor.apply();
+
+        Rlog.i(TAG, "updateNoSimEcbmSupported preference updated slotIndex=" + slotIndex
+                + ", supported=" + carrierConfig);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 3ab9fd7..a5127f2 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -2527,6 +2527,10 @@
             }
             updateImsRegistrationInfo(REGISTRATION_STATE_NOT_REGISTERED,
                     imsRadioTech, suggestedModemAction);
+
+            // Clear the phone number from P-Associated-Uri
+            setCurrentSubscriberUris(null);
+            clearPhoneNumberForSourceIms();
         }
 
         @Override
@@ -2537,6 +2541,18 @@
         }
     };
 
+    /** Clear the IMS phone number from IMS associated Uris when IMS registration is lost. */
+    @VisibleForTesting
+    public void clearPhoneNumberForSourceIms() {
+        int subId = getSubId();
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            return;
+        }
+
+        if (DBG) logd("clearPhoneNumberForSourceIms");
+        mSubscriptionManagerService.setNumberFromIms(subId, new String(""));
+    }
+
     /** Sets the IMS phone number from IMS associated URIs, if any found. */
     @VisibleForTesting
     public void setPhoneNumberForSourceIms(Uri[] uris) {
@@ -2706,9 +2722,13 @@
             @RegistrationManager.SuggestedAction int suggestedAction) {
 
         if (regState == mImsRegistrationState) {
+            // In NOT_REGISTERED state, the current PLMN can be blocked with a suggested action.
+            // But in this case, the same behavior is able to occur in different PLMNs with
+            // same radio tech and suggested action.
             if ((regState == REGISTRATION_STATE_REGISTERED && imsRadioTech == mImsRegistrationTech)
                     || (regState == REGISTRATION_STATE_NOT_REGISTERED
-                            && suggestedAction == mImsRegistrationSuggestedAction
+                            && suggestedAction == SUGGESTED_ACTION_NONE
+                            && mImsRegistrationSuggestedAction == SUGGESTED_ACTION_NONE
                             && imsRadioTech == mImsDeregistrationTech)) {
                 // Filter duplicate notification.
                 return;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index 7125763..a7a9129 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -57,14 +57,6 @@
     }
 
     @Override
-    public void getIccSlotsStatus(Message result) {
-    }
-
-    @Override
-    public void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result) {
-    }
-
-    @Override
     public void supplyIccPin(String pin, Message result) {
     }
 
@@ -107,10 +99,6 @@
     }
 
     @Override
-    @Deprecated public void getPDPContextList(Message result) {
-    }
-
-    @Override
     public void getDataCallList(Message result) {
     }
 
@@ -134,14 +122,6 @@
     }
 
     @Override
-    public void getIMEI(Message result) {
-    }
-
-    @Override
-    public void getIMEISV(Message result) {
-    }
-
-    @Override
     public void hangupConnection (int gsmIndex, Message result) {
     }
 
@@ -189,15 +169,6 @@
     public void getLastCallFailCause (Message result) {
     }
 
-    @Deprecated
-    @Override
-    public void getLastPdpFailCause (Message result) {
-    }
-
-    @Override
-    public void getLastDataCallFailCause (Message result) {
-    }
-
     @Override
     public void setMute (boolean enableMute, Message response) {
     }
@@ -286,10 +257,9 @@
     }
 
     @Override
-    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
-            boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
-            boolean matchAllRuleAllowed, Message result) {
+    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean allowRoaming,
+            int reason, LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo,
+            TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, Message result) {
     }
 
     @Override
@@ -416,18 +386,6 @@
     }
 
     @Override
-    public void resetRadio(Message result) {
-    }
-
-    @Override
-    public void invokeOemRilRequestRaw(byte[] data, Message response) {
-    }
-
-    @Override
-    public void invokeOemRilRequestStrings(String[] strings, Message response) {
-    }
-
-    @Override
     public void setBandMode (int bandMode, Message response) {
     }
 
@@ -596,11 +554,11 @@
     }
 
     @Override
-    public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
+    public void setInitialAttachApn(DataProfile dataProfile, Message result) {
     }
 
     @Override
-    public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
+    public void setDataProfile(DataProfile[] dps, Message result) {
     }
 
     @Override
@@ -640,18 +598,6 @@
     }
 
     @Override
-    public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
-    }
-
-    @Override
-    public void stopLceService(Message result) {
-    }
-
-    @Override
-    public void pullLceData(Message result) {
-    }
-
-    @Override
     public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
                                                 Message result) {
     }
diff --git a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
index 0fd97ba..387495e 100644
--- a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
@@ -42,7 +42,7 @@
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.telephony.Rlog;
 
-import java.util.List;
+import java.util.Set;
 
 /**
  * Generates metrics related to data stall recovery events per phone ID for the pushed atom.
@@ -103,8 +103,9 @@
         dataNetworkController.registerDataNetworkControllerCallback(
                 new DataNetworkControllerCallback(mHandler::post) {
                 @Override
-                public void onInternetDataNetworkConnected(
-                        @NonNull List<DataNetwork> internetNetworks) {
+                public void onConnectedInternetDataNetworksChanged(
+                        @NonNull Set<DataNetwork> internetNetworks) {
+                    mIfaceName = null;
                     for (DataNetwork dataNetwork : internetNetworks) {
                         mIfaceName = dataNetwork.getLinkProperties().getInterfaceName();
                         break;
@@ -112,11 +113,6 @@
                 }
 
                 @Override
-                public void onInternetDataNetworkDisconnected() {
-                    mIfaceName = null;
-                }
-
-                @Override
                 public void onPhysicalLinkStatusChanged(@LinkStatus int status) {
                     mInternetLinkStatus = status;
                 }
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index 9fd8fc4..a04a63a 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -97,6 +97,8 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccSlot;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.telephony.Rlog;
 
@@ -193,13 +195,13 @@
             registerAtom(GBA_EVENT);
             registerAtom(PER_SIM_STATUS);
             registerAtom(OUTGOING_SHORT_CODE_SMS);
+            registerAtom(EMERGENCY_NUMBERS_INFO);
             registerAtom(SATELLITE_CONTROLLER);
             registerAtom(SATELLITE_SESSION);
             registerAtom(SATELLITE_INCOMING_DATAGRAM);
             registerAtom(SATELLITE_OUTGOING_DATAGRAM);
             registerAtom(SATELLITE_PROVISION);
             registerAtom(SATELLITE_SOS_MESSAGE_RECOMMENDER);
-            registerAtom(EMERGENCY_NUMBERS_INFO);
             Rlog.d(TAG, "registered");
         } else {
             Rlog.e(TAG, "could not get StatsManager, atoms not registered");
@@ -276,6 +278,8 @@
                 return pullPerSimStatus(data);
             case OUTGOING_SHORT_CODE_SMS:
                 return pullOutgoingShortCodeSms(data);
+            case EMERGENCY_NUMBERS_INFO:
+                return pullEmergencyNumbersInfo(data);
             case SATELLITE_CONTROLLER:
                 return pullSatelliteController(data);
             case SATELLITE_SESSION:
@@ -288,8 +292,6 @@
                 return pullSatelliteProvision(data);
             case SATELLITE_SOS_MESSAGE_RECOMMENDER:
                 return pullSatelliteSosMessageRecommender(data);
-            case EMERGENCY_NUMBERS_INFO:
-                return pullEmergencyNumbersInfo(data);
             default:
                 Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag));
                 return StatsManager.PULL_SKIP;
@@ -383,7 +385,9 @@
                         SIM_SLOT_STATE,
                         state.numActiveSlots,
                         state.numActiveSims,
-                        state.numActiveEsims));
+                        state.numActiveEsims,
+                        state.numActiveEsimSlots,
+                        state.numActiveMepSlots));
         return StatsManager.PULL_SUCCESS;
     }
 
@@ -572,9 +576,14 @@
         boolean hasDedicatedManagedProfileSub = Arrays.stream(phones)
                 .anyMatch(Phone::isManagedProfile);
 
+        UiccSlot[] slots = UiccController.getInstance().getUiccSlots();
+        int mepSupportedSlotCount = (int) Arrays.stream(slots)
+                .filter(UiccSlot::isMultipleEnabledProfileSupported)
+                .count();
+
         data.add(TelephonyStatsLog.buildStatsEvent(DEVICE_TELEPHONY_PROPERTIES, true,
                 isAutoDataSwitchOn, mStorage.getAutoDataSwitchToggleCount(),
-                hasDedicatedManagedProfileSub));
+                hasDedicatedManagedProfileSub, mepSupportedSlotCount));
         return StatsManager.PULL_SUCCESS;
     }
 
@@ -797,6 +806,21 @@
         }
     }
 
+    private int pullEmergencyNumbersInfo(List<StatsEvent> data) {
+        boolean isDataLogged = false;
+        for (Phone phone : getPhonesIfAny()) {
+            if (phone != null) {
+                EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+                if (tracker != null) {
+                    EmergencyNumbersInfo[] numList = tracker.getEmergencyNumbersProtoArray();
+                    Arrays.stream(numList).forEach(number -> data.add(buildStatsEvent(number)));
+                    isDataLogged = true;
+                }
+            }
+        }
+        return isDataLogged ? StatsManager.PULL_SUCCESS : StatsManager.PULL_SKIP;
+    }
+
     private int pullSatelliteController(List<StatsEvent> data) {
         SatelliteController[] controllerAtoms =
                 mStorage.getSatelliteControllerStats(MIN_COOLDOWN_MILLIS);
@@ -877,21 +901,6 @@
         }
     }
 
-    private int pullEmergencyNumbersInfo(List<StatsEvent> data) {
-        boolean isDataLogged = false;
-        for (Phone phone : getPhonesIfAny()) {
-            if (phone != null) {
-                EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
-                if (tracker != null) {
-                    EmergencyNumbersInfo[] numList = tracker.getEmergencyNumbersProtoArray();
-                    Arrays.stream(numList).forEach(number -> data.add(buildStatsEvent(number)));
-                    isDataLogged = true;
-                }
-            }
-        }
-        return isDataLogged ? StatsManager.PULL_SUCCESS : StatsManager.PULL_SKIP;
-    }
-
     /** Registers a pulled atom ID {@code atomId}. */
     private void registerAtom(int atomId) {
         mStatsManager.setPullAtomCallback(atomId, /* metadata= */ null,
@@ -1246,6 +1255,21 @@
                 shortCodeSms.shortCodeSmsCount);
     }
 
+    private static StatsEvent buildStatsEvent(EmergencyNumbersInfo emergencyNumber) {
+        return TelephonyStatsLog.buildStatsEvent(
+                EMERGENCY_NUMBERS_INFO,
+                emergencyNumber.isDbVersionIgnored,
+                emergencyNumber.assetVersion,
+                emergencyNumber.otaVersion,
+                emergencyNumber.number,
+                emergencyNumber.countryIso,
+                emergencyNumber.mnc,
+                emergencyNumber.route,
+                emergencyNumber.urns,
+                emergencyNumber.serviceCategories,
+                emergencyNumber.sources);
+    }
+
     private static StatsEvent buildStatsEvent(SatelliteController satelliteController) {
         return TelephonyStatsLog.buildStatsEvent(
                 SATELLITE_CONTROLLER,
@@ -1312,21 +1336,6 @@
                 stats.count);
     }
 
-    private static StatsEvent buildStatsEvent(EmergencyNumbersInfo emergencyNumber) {
-        return TelephonyStatsLog.buildStatsEvent(
-                EMERGENCY_NUMBERS_INFO,
-                emergencyNumber.isDbVersionIgnored,
-                emergencyNumber.assetVersion,
-                emergencyNumber.otaVersion,
-                emergencyNumber.number,
-                emergencyNumber.countryIso,
-                emergencyNumber.mnc,
-                emergencyNumber.route,
-                emergencyNumber.urns,
-                emergencyNumber.serviceCategories,
-                emergencyNumber.sources);
-    }
-
     /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
     static Phone[] getPhonesIfAny() {
         try {
diff --git a/src/java/com/android/internal/telephony/metrics/RadioPowerStateStats.java b/src/java/com/android/internal/telephony/metrics/RadioPowerStateStats.java
new file mode 100644
index 0000000..f50c2b2
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/RadioPowerStateStats.java
@@ -0,0 +1,52 @@
+/*
+ * 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.internal.telephony.metrics;
+
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_RADIO_POWER_STATE_CHANGED;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_RADIO_POWER_STATE_CHANGED__STATE__RADIO_POWER_STATE_OFF;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_RADIO_POWER_STATE_CHANGED__STATE__RADIO_POWER_STATE_ON;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_RADIO_POWER_STATE_CHANGED__STATE__RADIO_POWER_STATE_UNAVAILABLE;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_RADIO_POWER_STATE_CHANGED__STATE__RADIO_POWER_STATE_UNKNOWN;
+
+import android.telephony.Annotation;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.TelephonyStatsLog;
+
+/** Logs cellular radio power state changes to statsd */
+public class RadioPowerStateStats {
+
+    /** Logs cellular radio power state changes to statsd */
+    public static void onRadioStateChanged(@Annotation.RadioPowerState int state) {
+        TelephonyStatsLog.write(CELLULAR_RADIO_POWER_STATE_CHANGED,
+                radioPowerStateToProtoEnum(state));
+    }
+
+    private static int radioPowerStateToProtoEnum(@Annotation.RadioPowerState int state) {
+        switch (state) {
+            case TelephonyManager.RADIO_POWER_OFF:
+                return CELLULAR_RADIO_POWER_STATE_CHANGED__STATE__RADIO_POWER_STATE_OFF;
+            case TelephonyManager.RADIO_POWER_ON:
+                return CELLULAR_RADIO_POWER_STATE_CHANGED__STATE__RADIO_POWER_STATE_ON;
+            case TelephonyManager.RADIO_POWER_UNAVAILABLE:
+                return CELLULAR_RADIO_POWER_STATE_CHANGED__STATE__RADIO_POWER_STATE_UNAVAILABLE;
+            default:
+                return CELLULAR_RADIO_POWER_STATE_CHANGED__STATE__RADIO_POWER_STATE_UNKNOWN;
+        }
+    }
+
+}
diff --git a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
index f2f1190..982a8f5 100644
--- a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
+++ b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
@@ -45,7 +45,7 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
 import com.android.telephony.Rlog;
 
-import java.util.List;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -59,6 +59,7 @@
     private final Phone mPhone;
     private final PersistAtomsStorage mStorage;
     private final DeviceStateHelper mDeviceStateHelper;
+    private boolean mExistAnyConnectedInternetPdn;
 
     public ServiceStateStats(Phone phone) {
         super(Runnable::run);
@@ -98,14 +99,14 @@
         mPhone.getDataNetworkController().registerDataNetworkControllerCallback(this);
     }
 
-    /** Updates service state when internet pdn gets connected. */
-    public void onInternetDataNetworkConnected(@NonNull List<DataNetwork> internetNetworks) {
-        onInternetDataNetworkChanged(true);
-    }
-
-    /** Updates service state when internet pdn gets disconnected. */
-    public void onInternetDataNetworkDisconnected() {
-        onInternetDataNetworkChanged(false);
+    /** Updates service state when internet pdn changed. */
+    @Override
+    public void onConnectedInternetDataNetworksChanged(@NonNull Set<DataNetwork> internetNetworks) {
+        boolean existAnyConnectedInternetPdn = !internetNetworks.isEmpty();
+        if (mExistAnyConnectedInternetPdn != existAnyConnectedInternetPdn) {
+            mExistAnyConnectedInternetPdn = existAnyConnectedInternetPdn;
+            onInternetDataNetworkChanged(mExistAnyConnectedInternetPdn);
+        }
     }
 
     /** Updates the current service state. */
diff --git a/src/java/com/android/internal/telephony/metrics/SimSlotState.java b/src/java/com/android/internal/telephony/metrics/SimSlotState.java
index f840894..59237e1 100644
--- a/src/java/com/android/internal/telephony/metrics/SimSlotState.java
+++ b/src/java/com/android/internal/telephony/metrics/SimSlotState.java
@@ -23,6 +23,9 @@
 import com.android.internal.telephony.uicc.UiccSlot;
 import com.android.telephony.Rlog;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 /** Snapshots and stores the current SIM state. */
 public class SimSlotState {
     private static final String TAG = SimSlotState.class.getSimpleName();
@@ -30,31 +33,41 @@
     public final int numActiveSlots;
     public final int numActiveSims;
     public final int numActiveEsims;
+    public final int numActiveEsimSlots;
+    public final int numActiveMepSlots;
 
     /** Returns the current SIM state. */
     public static SimSlotState getCurrentState() {
         int numActiveSlots = 0;
         int numActiveSims = 0;
         int numActiveEsims = 0;
+        int numActiveEsimSlots = 0;
+        int numActiveMepSlots = 0;
         UiccController uiccController = UiccController.getInstance();
         // since we cannot hold lock insider UiccController, using getUiccSlots() for length only
         for (int i = 0; i < uiccController.getUiccSlots().length; i++) {
             UiccSlot slot = uiccController.getUiccSlot(i);
             if (slot != null && slot.isActive()) {
                 numActiveSlots++;
+                if (slot.isEuicc()) {
+                    numActiveEsimSlots++;
+                }
                 // avoid CardState.isCardPresent() since this should not include restricted cards
                 if (slot.getCardState() == CardState.CARDSTATE_PRESENT) {
                     if (slot.isEuicc()) {
                         // need to check active profiles besides the presence of eSIM cards
                         UiccCard card = slot.getUiccCard();
                         if (card != null) {
-                            // Check each port on the EuiccCard
-                            UiccPort[] uiccPorts = card.getUiccPortList();
-                            for (UiccPort port : uiccPorts) {
-                                if (port != null && port.getNumApplications() > 0) {
-                                    numActiveSims++;
-                                    numActiveEsims++;
-                                }
+                            int numActiveProfiles = (int) Arrays.stream(card.getUiccPortList())
+                                    .filter(Objects::nonNull)
+                                    .map(UiccPort::getUiccProfile)
+                                    .filter(Objects::nonNull)
+                                    .filter(profile -> profile.getNumApplications() > 0)
+                                    .count();
+                            numActiveSims += numActiveProfiles;
+                            numActiveEsims += numActiveProfiles;
+                            if (numActiveProfiles > 1) {
+                                numActiveMepSlots++;
                             }
                         }
                     } else {
@@ -64,13 +77,25 @@
                 }
             }
         }
-        return new SimSlotState(numActiveSlots, numActiveSims, numActiveEsims);
+        return new SimSlotState(
+                numActiveSlots,
+                numActiveSims,
+                numActiveEsims,
+                numActiveEsimSlots,
+                numActiveMepSlots);
     }
 
-    private SimSlotState(int numActiveSlots, int numActiveSims, int numActiveEsims) {
+    private SimSlotState(
+            int numActiveSlots,
+            int numActiveSims,
+            int numActiveEsims,
+            int numActiveEsimSlots,
+            int numActiveMepSlots) {
         this.numActiveSlots = numActiveSlots;
         this.numActiveSims = numActiveSims;
         this.numActiveEsims = numActiveEsims;
+        this.numActiveEsimSlots = numActiveEsimSlots;
+        this.numActiveMepSlots = numActiveMepSlots;
     }
 
     /** Returns whether the given phone is using a eSIM. */
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index 5d8e027..5de9902 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -66,6 +66,8 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.analytics.TelephonyAnalytics;
+import com.android.internal.telephony.analytics.TelephonyAnalytics.CallAnalytics;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
@@ -211,6 +213,19 @@
                     proto.disconnectExtraCode = conn.getPreciseDisconnectCause();
                     proto.disconnectExtraMessage = conn.getVendorDisconnectCause();
                     proto.callDuration = classifyCallDuration(conn.getDurationMillis());
+                    if (mPhone != null) {
+                        TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+                        if (telephonyAnalytics != null) {
+                            CallAnalytics callAnalytics = telephonyAnalytics.getCallAnalytics();
+                            if (callAnalytics != null) {
+                                callAnalytics.onCallTerminated(proto.isEmergency,
+                                        false /* isOverIms */,
+                                        getVoiceRatWithVoNRFix(mPhone, mPhone.getServiceState(),
+                                                proto.bearerAtEnd),
+                                        proto.simSlotIndex, proto.disconnectReasonCode);
+                            }
+                        }
+                    }
                     finishCall(id);
                 }
             }
@@ -640,6 +655,18 @@
         proto.disconnectExtraCode = reasonInfo.mExtraCode;
         proto.disconnectExtraMessage = ImsStats.filterExtraMessage(reasonInfo.mExtraMessage);
         proto.callDuration = classifyCallDuration(durationMillis);
+        if (mPhone != null) {
+            TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
+            if (telephonyAnalytics != null) {
+                CallAnalytics callAnalytics = telephonyAnalytics.getCallAnalytics();
+                if (callAnalytics != null) {
+                    callAnalytics.onCallTerminated(proto.isEmergency, true /* isOverIms */ ,
+                            getVoiceRatWithVoNRFix(
+                                    mPhone, mPhone.getServiceState(), proto.bearerAtEnd),
+                            proto.simSlotIndex, proto.disconnectReasonCode);
+                }
+            }
+        }
         finishCall(id);
     }
 
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramController.java b/src/java/com/android/internal/telephony/satellite/DatagramController.java
index e7f09c2..d790ca9 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramController.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramController.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING;
+
 import android.annotation.NonNull;
 import android.content.Context;
 import android.os.Build;
@@ -46,6 +49,8 @@
     public static final long MAX_DATAGRAM_ID = (long) Math.pow(2, 16);
     public static final int ROUNDING_UNIT = 10;
     public static final long SATELLITE_ALIGN_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
+    public static final long DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT =
+            TimeUnit.SECONDS.toMillis(60);
     private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
     private static final boolean DEBUG = !"user".equals(Build.TYPE);
 
@@ -59,7 +64,7 @@
     @GuardedBy("mLock")
     private int mSendPendingCount = 0;
     @GuardedBy("mLock")
-    private int mSendErrorCode = SatelliteManager.SATELLITE_ERROR_NONE;
+    private int mSendErrorCode = SatelliteManager.SATELLITE_RESULT_SUCCESS;
     /** Variables used to update onReceiveDatagramStateChanged(). */
     @GuardedBy("mLock")
     private int mReceiveSubId;
@@ -69,11 +74,15 @@
     @GuardedBy("mLock")
     private int mReceivePendingCount = 0;
     @GuardedBy("mLock")
-    private int mReceiveErrorCode = SatelliteManager.SATELLITE_ERROR_NONE;
+    private int mReceiveErrorCode = SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
     private SatelliteDatagram mDemoModeDatagram;
     private boolean mIsDemoMode = false;
     private long mAlignTimeoutDuration = SATELLITE_ALIGN_TIMEOUT;
+    private long mDatagramWaitTimeForConnectedState = DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT;
+    @GuardedBy("mLock")
+    @SatelliteManager.SatelliteModemState
+    private int mSatelltieModemState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
 
     /**
      * @return The singleton instance of DatagramController.
@@ -130,9 +139,9 @@
      * @param subId The subId of the subscription to register for incoming satellite datagrams.
      * @param callback The callback to handle incoming datagrams over satellite.
      *
-     * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+     * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
-    @SatelliteManager.SatelliteError public int registerForSatelliteDatagram(int subId,
+    @SatelliteManager.SatelliteResult public int registerForSatelliteDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
         return mDatagramReceiver.registerForSatelliteDatagram(subId, callback);
     }
@@ -159,7 +168,7 @@
      * long, SatelliteDatagram, int, Consumer)}
      *
      * @param subId The subId of the subscription used for receiving datagrams.
-     * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+     * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
     public void pollPendingSatelliteDatagrams(int subId, @NonNull Consumer<Integer> callback) {
         mDatagramReceiver.pollPendingSatelliteDatagrams(subId, callback);
@@ -182,7 +191,7 @@
      *                 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 callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+     * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
     public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
             @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
@@ -267,13 +276,16 @@
      * @param state Current satellite modem state.
      */
     public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) {
+        synchronized (mLock) {
+            mSatelltieModemState = state;
+        }
         mDatagramDispatcher.onSatelliteModemStateChanged(state);
         mDatagramReceiver.onSatelliteModemStateChanged(state);
     }
 
-    void onDeviceAlignedWithSatellite(boolean isAligned) {
-        mDatagramDispatcher.onDeviceAlignedWithSatellite(isAligned);
-        mDatagramReceiver.onDeviceAlignedWithSatellite(isAligned);
+    void setDeviceAlignedWithSatellite(boolean isAligned) {
+        mDatagramDispatcher.setDeviceAlignedWithSatellite(isAligned);
+        mDatagramReceiver.setDeviceAlignedWithSatellite(isAligned);
     }
 
     @VisibleForTesting
@@ -284,17 +296,35 @@
         }
     }
 
+    /**
+     * Check if Telephony needs to wait for the modem satellite connected to a satellite network
+     * before transferring datagrams via satellite.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean needsWaitingForSatelliteConnected() {
+        synchronized (mLock) {
+            if (SatelliteController.getInstance().isSatelliteAttachRequired()
+                    && mSatelltieModemState != SATELLITE_MODEM_STATE_CONNECTED
+                    && mSatelltieModemState != SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING) {
+                return true;
+            }
+            return false;
+        }
+    }
+
     public boolean isSendingInIdleState() {
         synchronized (mLock) {
-            return mSendDatagramTransferState ==
-                    SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+            return (mSendDatagramTransferState
+                    == SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE)
+                    && (mDatagramDispatcher.getPendingDatagramCount() == 0);
         }
     }
 
     public boolean isPollingInIdleState() {
         synchronized (mLock) {
-            return mReceiveDatagramTransferState ==
-                    SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+            return (mReceiveDatagramTransferState
+                    == SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE)
+                    && !mDatagramReceiver.isPollingPending();
         }
     }
 
@@ -336,6 +366,11 @@
         return mAlignTimeoutDuration;
     }
 
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public long getDatagramWaitTimeForConnectedState() {
+        return mDatagramWaitTimeForConnectedState;
+    }
+
     /**
      * This API can be used by only CTS to update the timeout duration in milliseconds whether
      * the device is aligned with the satellite for demo mode
@@ -351,6 +386,7 @@
 
         logd("setSatelliteDeviceAlignedTimeoutDuration: timeoutMillis=" + timeoutMillis);
         mAlignTimeoutDuration = timeoutMillis;
+        mDatagramWaitTimeForConnectedState = timeoutMillis;
         return true;
     }
 
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index 77b410d..b8d9ef4 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
+
 import static com.android.internal.telephony.satellite.DatagramController.ROUNDING_UNIT;
 
 import android.annotation.NonNull;
@@ -51,6 +53,7 @@
     private static final int CMD_SEND_SATELLITE_DATAGRAM = 1;
     private static final int EVENT_SEND_SATELLITE_DATAGRAM_DONE = 2;
     private static final int EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT = 3;
+    private static final int EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT = 4;
 
     @NonNull private static DatagramDispatcher sInstance;
     @NonNull private final Context mContext;
@@ -212,20 +215,20 @@
                         mDatagramController.updateSendStatus(argument.subId,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
                                 getPendingDatagramCount(),
-                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                                SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
                         mDatagramController.updateSendStatus(argument.subId,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
-                                0, SatelliteManager.SATELLITE_ERROR_NONE);
+                                0, SatelliteManager.SATELLITE_RESULT_SUCCESS);
 
                         // report phone == null case
                         reportSendDatagramCompleted(argument,
-                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                                SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
                         argument.callback.accept(
-                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                                SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
 
                         // Abort sending all the pending datagrams
                         abortSendingPendingDatagrams(argument.subId,
-                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                                SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
                     }
                 }
                 break;
@@ -239,7 +242,7 @@
                         (SendSatelliteDatagramArgument) request.argument;
 
                 synchronized (mLock) {
-                    if (mIsDemoMode && (error == SatelliteManager.SATELLITE_ERROR_NONE)) {
+                    if (mIsDemoMode && (error == SatelliteManager.SATELLITE_RESULT_SUCCESS)) {
                         if (argument.skipCheckingSatelliteAligned) {
                             logd("Satellite was already aligned. No need to check alignment again");
                         } else if (!mIsAligned) {
@@ -262,7 +265,7 @@
                         mPendingNonEmergencyDatagramsMap.remove(argument.datagramId);
                     }
 
-                    if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                    if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                         // Update send status for current datagram
                         mDatagramController.updateSendStatus(argument.subId,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
@@ -278,7 +281,7 @@
                         } else {
                             mDatagramController.updateSendStatus(argument.subId,
                                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
-                                    0, SatelliteManager.SATELLITE_ERROR_NONE);
+                                    0, SatelliteManager.SATELLITE_RESULT_SUCCESS);
                             // Send response for current datagram
                             argument.callback.accept(error);
                         }
@@ -289,7 +292,7 @@
                                 getPendingDatagramCount(), error);
                         mDatagramController.updateSendStatus(argument.subId,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
-                                0, SatelliteManager.SATELLITE_ERROR_NONE);
+                                0, SatelliteManager.SATELLITE_RESULT_SUCCESS);
                         // Send response for current datagram
                         // after updating datagram transfer state internally.
                         argument.callback.accept(error);
@@ -297,7 +300,7 @@
                         mControllerMetricsStats.reportOutgoingDatagramFailCount(
                                 argument.datagramType);
                         abortSendingPendingDatagrams(argument.subId,
-                                SatelliteManager.SATELLITE_REQUEST_ABORTED);
+                                SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
                     }
                 }
                 break;
@@ -308,6 +311,10 @@
                 break;
             }
 
+            case EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT:
+                handleEventDatagramWaitForConnectedStateTimedOut();
+                break;
+
             default:
                 logw("DatagramDispatcherHandler: unexpected message code: " + msg.what);
                 break;
@@ -328,7 +335,7 @@
      *                 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 callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+     * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
     public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
             @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
@@ -350,13 +357,18 @@
                 mPendingNonEmergencyDatagramsMap.put(datagramId, datagramArgs);
             }
 
-            // Modem can be busy receiving datagrams, so send datagram only when modem is not busy.
-            if (!mSendingDatagramInProgress && mDatagramController.isPollingInIdleState()) {
+            if (mDatagramController.needsWaitingForSatelliteConnected()) {
+                logd("sendSatelliteDatagram: wait for satellite connected");
+                SatelliteSessionController.getInstance().onSatelliteDatagramsTransferRequested();
+                startDatagramWaitForConnectedStateTimer();
+            } else if (!mSendingDatagramInProgress && mDatagramController.isPollingInIdleState()) {
+                // Modem can be busy receiving datagrams, so send datagram only when modem is
+                // not busy.
                 mSendingDatagramInProgress = true;
                 datagramArgs.setDatagramStartTime();
                 mDatagramController.updateSendStatus(subId,
                         SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
-                        getPendingDatagramCount(), SatelliteManager.SATELLITE_ERROR_NONE);
+                        getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
                 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArgs, phone);
             }
         }
@@ -378,7 +390,7 @@
     }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    protected void onDeviceAlignedWithSatellite(boolean isAligned) {
+    protected void setDeviceAlignedWithSatellite(boolean isAligned) {
         if (mIsDemoMode) {
             synchronized (mLock) {
                 mIsAligned = isAligned;
@@ -426,7 +438,7 @@
             @NonNull DatagramDispatcherHandlerRequest request) {
         SatelliteManager.SatelliteException exception =
                 new SatelliteManager.SatelliteException(
-                        SatelliteManager.SATELLITE_NOT_REACHABLE);
+                        SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE);
         Message message = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request);
         AsyncResult.forMessage(message, null, exception);
         message.sendToTarget();
@@ -474,7 +486,7 @@
             datagramArg.setDatagramStartTime();
             mDatagramController.updateSendStatus(datagramArg.subId,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
-                    getPendingDatagramCount(), SatelliteManager.SATELLITE_ERROR_NONE);
+                    getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
             sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArg, phone);
         }
     }
@@ -488,7 +500,7 @@
     @GuardedBy("mLock")
     private void sendErrorCodeAndCleanupPendingDatagrams(
             LinkedHashMap<Long, SendSatelliteDatagramArgument> pendingDatagramsMap,
-            @SatelliteManager.SatelliteError int errorCode) {
+            @SatelliteManager.SatelliteResult int errorCode) {
         if (pendingDatagramsMap.size() == 0) {
             return;
         }
@@ -515,7 +527,7 @@
      */
     @GuardedBy("mLock")
     private void abortSendingPendingDatagrams(int subId,
-            @SatelliteManager.SatelliteError int errorCode) {
+            @SatelliteManager.SatelliteResult int errorCode) {
         logd("abortSendingPendingDatagrams()");
         sendErrorCodeAndCleanupPendingDatagrams(mPendingEmergencyDatagramsMap, errorCode);
         sendErrorCodeAndCleanupPendingDatagrams(mPendingNonEmergencyDatagramsMap, errorCode);
@@ -525,9 +537,10 @@
      * Return pending datagram count
      * @return pending datagram count
      */
-    @GuardedBy("mLock")
-    private int getPendingDatagramCount() {
-        return mPendingEmergencyDatagramsMap.size() + mPendingNonEmergencyDatagramsMap.size();
+    public int getPendingDatagramCount() {
+        synchronized (mLock) {
+            return mPendingEmergencyDatagramsMap.size() + mPendingNonEmergencyDatagramsMap.size();
+        }
     }
 
     /**
@@ -545,7 +558,7 @@
     }
 
     private void reportSendDatagramCompleted(@NonNull SendSatelliteDatagramArgument argument,
-            @NonNull @SatelliteManager.SatelliteError int resultCode) {
+            @NonNull @SatelliteManager.SatelliteResult int resultCode) {
         SatelliteStats.getInstance().onSatelliteOutgoingDatagramMetrics(
                 new SatelliteStats.SatelliteOutgoingDatagramParams.Builder()
                         .setDatagramType(argument.datagramType)
@@ -579,6 +592,12 @@
             } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_IDLE) {
                 sendPendingDatagrams();
             }
+
+            if (state == SATELLITE_MODEM_STATE_CONNECTED
+                    && isDatagramWaitForConnectedStateTimerStarted()) {
+                stopDatagramWaitForConnectedStateTimer();
+                sendPendingDatagrams();
+            }
         }
     }
 
@@ -589,20 +608,49 @@
             mDatagramController.updateSendStatus(
                     SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
-                    getPendingDatagramCount(), SatelliteManager.SATELLITE_REQUEST_ABORTED);
+                    getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
         }
         mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
-                0, SatelliteManager.SATELLITE_ERROR_NONE);
+                0, SatelliteManager.SATELLITE_RESULT_SUCCESS);
         abortSendingPendingDatagrams(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                SatelliteManager.SATELLITE_REQUEST_ABORTED);
+                SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
 
         stopSatelliteAlignedTimer();
+        stopDatagramWaitForConnectedStateTimer();
         mIsDemoMode = false;
         mSendSatelliteDatagramRequest = null;
         mIsAligned = false;
     }
 
+    private void startDatagramWaitForConnectedStateTimer() {
+        if (isDatagramWaitForConnectedStateTimerStarted()) {
+            logd("DatagramWaitForConnectedStateTimer is already started");
+            return;
+        }
+        sendMessageDelayed(obtainMessage(
+                        EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT),
+                mDatagramController.getDatagramWaitTimeForConnectedState());
+    }
+
+    private void stopDatagramWaitForConnectedStateTimer() {
+        removeMessages(EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT);
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public boolean isDatagramWaitForConnectedStateTimerStarted() {
+        return hasMessages(EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT);
+    }
+
+    private void handleEventDatagramWaitForConnectedStateTimedOut() {
+        logw("Timed out to wait for satellite connected before sending datagrams");
+        synchronized (mLock) {
+            abortSendingPendingDatagrams(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                    SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE);
+            SatelliteSessionController.getInstance().onDatagramWaitForConnectedStateTimerTimedOut();
+        }
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
index 06eede1..cd98f43 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
+
 import static com.android.internal.telephony.satellite.DatagramController.ROUNDING_UNIT;
 
 import android.annotation.NonNull;
@@ -65,6 +67,7 @@
     private static final int CMD_POLL_PENDING_SATELLITE_DATAGRAMS = 1;
     private static final int EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE = 2;
     private static final int EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT = 3;
+    private static final int EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT = 4;
 
     /** Key used to read/write satellite datagramId in shared preferences. */
     private static final String SATELLITE_DATAGRAM_ID_KEY = "satellite_datagram_id_key";
@@ -82,7 +85,10 @@
     private boolean mIsDemoMode = false;
     @GuardedBy("mLock")
     private boolean mIsAligned = false;
-    private DatagramReceiverHandlerRequest mPollPendingSatelliteDatagramsRequest = null;
+    @Nullable
+    private DatagramReceiverHandlerRequest mDemoPollPendingSatelliteDatagramsRequest = null;
+    @Nullable
+    private DatagramReceiverHandlerRequest mPendingPollSatelliteDatagramsRequest = null;
     private final Object mLock = new Object();
 
     /**
@@ -139,12 +145,6 @@
         } catch (Exception e) {
             loge("Cannot get default shared preferences: " + e);
         }
-
-        if ((mSharedPreferences != null) &&
-                (!mSharedPreferences.contains(SATELLITE_DATAGRAM_ID_KEY))) {
-            mSharedPreferences.edit().putLong(SATELLITE_DATAGRAM_ID_KEY, mNextDatagramId.get())
-                    .commit();
-        }
     }
 
     private static final class DatagramReceiverHandlerRequest {
@@ -335,11 +335,11 @@
                     if (pendingCount <= 0 && satelliteDatagram == null) {
                         sInstance.mDatagramController.updateReceiveStatus(mSubId,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE,
-                                pendingCount, SatelliteManager.SATELLITE_ERROR_NONE);
+                                pendingCount, SatelliteManager.SATELLITE_RESULT_SUCCESS);
                     } else if (satelliteDatagram != null) {
                         sInstance.mDatagramController.updateReceiveStatus(mSubId,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS,
-                                pendingCount, SatelliteManager.SATELLITE_ERROR_NONE);
+                                pendingCount, SatelliteManager.SATELLITE_RESULT_SUCCESS);
 
                         long datagramId = getDatagramId();
                         sInstance.mPendingAckCountHashMap.put(datagramId, getNumOfListeners());
@@ -356,13 +356,13 @@
                         });
 
                         sInstance.mControllerMetricsStats.reportIncomingDatagramCount(
-                                SatelliteManager.SATELLITE_ERROR_NONE);
+                                SatelliteManager.SATELLITE_RESULT_SUCCESS);
                     }
 
                     if (pendingCount <= 0) {
                         sInstance.mDatagramController.updateReceiveStatus(mSubId,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
-                                pendingCount, SatelliteManager.SATELLITE_ERROR_NONE);
+                                pendingCount, SatelliteManager.SATELLITE_RESULT_SUCCESS);
                     } else {
                         // Poll for pending datagrams
                         IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
@@ -378,7 +378,7 @@
 
                     // Send the captured data about incoming datagram to metric
                     sInstance.reportMetrics(
-                            satelliteDatagram, SatelliteManager.SATELLITE_ERROR_NONE);
+                            satelliteDatagram, SatelliteManager.SATELLITE_RESULT_SUCCESS);
                     break;
                 }
 
@@ -439,19 +439,19 @@
                     mDatagramController.updateReceiveStatus(request.subId,
                             SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
                             mDatagramController.getReceivePendingCount(),
-                            SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                            SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
 
                     mDatagramController.updateReceiveStatus(request.subId,
                             SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
                             mDatagramController.getReceivePendingCount(),
-                            SatelliteManager.SATELLITE_ERROR_NONE);
+                            SatelliteManager.SATELLITE_RESULT_SUCCESS);
 
-                    reportMetrics(null, SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                    reportMetrics(null, SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
                     mControllerMetricsStats.reportIncomingDatagramCount(
-                                    SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                                    SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
                     // Send response for current request
                     ((Consumer<Integer>) request.argument)
-                            .accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                            .accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
                 }
                 break;
             }
@@ -462,7 +462,7 @@
                 int error = SatelliteServiceUtils.getSatelliteError(ar,
                         "pollPendingSatelliteDatagrams");
 
-                if (mIsDemoMode && error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                if (mIsDemoMode && error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     SatelliteDatagram datagram = mDatagramController.getDemoModeDatagram();
                     final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(
                             request.subId, mContext);
@@ -476,12 +476,12 @@
                                 ar);
                         listenerHandler.sendMessage(message);
                     } else {
-                        error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+                        error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     }
                 }
 
                 logd("EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE error: " + error);
-                if (error != SatelliteManager.SATELLITE_ERROR_NONE) {
+                if (error != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     mDatagramController.updateReceiveStatus(request.subId,
                             SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
                             mDatagramController.getReceivePendingCount(), error);
@@ -489,7 +489,7 @@
                     mDatagramController.updateReceiveStatus(request.subId,
                             SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
                             mDatagramController.getReceivePendingCount(),
-                            SatelliteManager.SATELLITE_ERROR_NONE);
+                            SatelliteManager.SATELLITE_RESULT_SUCCESS);
 
                     reportMetrics(null, error);
                     mControllerMetricsStats.reportIncomingDatagramCount(error);
@@ -503,6 +503,14 @@
                 handleEventSatelliteAlignedTimeout((DatagramReceiverHandlerRequest) msg.obj);
                 break;
             }
+
+            case EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT:
+                handleEventDatagramWaitForConnectedStateTimedOut();
+                break;
+
+            default:
+                logw("DatagramDispatcherHandler: unexpected message code: " + msg.what);
+                break;
         }
     }
 
@@ -512,12 +520,12 @@
      * @param subId The subId of the subscription to register for incoming satellite datagrams.
      * @param callback The callback to handle incoming datagrams over satellite.
      *
-     * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+     * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
-    @SatelliteManager.SatelliteError public int registerForSatelliteDatagram(int subId,
+    @SatelliteManager.SatelliteResult public int registerForSatelliteDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
         if (!SatelliteController.getInstance().isSatelliteSupported()) {
-            return SatelliteManager.SATELLITE_NOT_SUPPORTED;
+            return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
 
         final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
@@ -539,7 +547,7 @@
 
         satelliteDatagramListenerHandler.addListener(callback);
         mSatelliteDatagramListenerHandlers.put(validSubId, satelliteDatagramListenerHandler);
-        return SatelliteManager.SATELLITE_ERROR_NONE;
+        return SatelliteManager.SATELLITE_RESULT_SUCCESS;
     }
 
     /**
@@ -582,32 +590,71 @@
      * #onSatelliteDatagramReceived(long, SatelliteDatagram, int, Consumer)}
      *
      * @param subId The subId of the subscription used for receiving datagrams.
-     * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+     * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
     public void pollPendingSatelliteDatagrams(int subId, @NonNull Consumer<Integer> callback) {
         if (!mDatagramController.isPollingInIdleState()) {
             // Poll request should be sent to satellite modem only when it is free.
             logd("pollPendingSatelliteDatagrams: satellite modem is busy receiving datagrams.");
-            callback.accept(SatelliteManager.SATELLITE_MODEM_BUSY);
+            callback.accept(SatelliteManager.SATELLITE_RESULT_MODEM_BUSY);
             return;
         }
-
         pollPendingSatelliteDatagramsInternal(subId, callback);
     }
 
+    /**
+     * Check if DatagramReceiver is waiting for satellite modem connected to a satellite network
+     * before pushing down the poll request to modem.
+     */
+    public boolean isPollingPending() {
+        synchronized (mLock) {
+            return (mPendingPollSatelliteDatagramsRequest != null);
+        }
+    }
+
+    private void handleSatelliteConnectedEvent() {
+        synchronized (mLock) {
+            if (isDatagramWaitForConnectedStateTimerStarted()) {
+                stopDatagramWaitForConnectedStateTimer();
+                if (mPendingPollSatelliteDatagramsRequest == null) {
+                    loge("handleSatelliteConnectedEvent: mPendingPollSatelliteDatagramsRequest is"
+                            + " null");
+                    return;
+                }
+
+                Consumer<Integer> callback =
+                        (Consumer<Integer>) mPendingPollSatelliteDatagramsRequest.argument;
+                pollPendingSatelliteDatagramsInternal(
+                        mPendingPollSatelliteDatagramsRequest.subId, callback);
+                mPendingPollSatelliteDatagramsRequest = null;
+            }
+        }
+    }
+
     private void pollPendingSatelliteDatagramsInternal(int subId,
             @NonNull Consumer<Integer> callback) {
         if (!mDatagramController.isSendingInIdleState()) {
             // Poll request should be sent to satellite modem only when it is free.
             logd("pollPendingSatelliteDatagrams: satellite modem is busy sending datagrams.");
-            callback.accept(SatelliteManager.SATELLITE_MODEM_BUSY);
+            callback.accept(SatelliteManager.SATELLITE_RESULT_MODEM_BUSY);
+            return;
+        }
+
+        if (mDatagramController.needsWaitingForSatelliteConnected()) {
+            logd("pollPendingSatelliteDatagrams: wait for satellite connected");
+            synchronized (mLock) {
+                mPendingPollSatelliteDatagramsRequest = new DatagramReceiverHandlerRequest(
+                        callback, SatelliteServiceUtils.getPhone(), subId);
+                SatelliteSessionController.getInstance().onSatelliteDatagramsTransferRequested();
+                startDatagramWaitForConnectedStateTimer();
+            }
             return;
         }
 
         mDatagramController.updateReceiveStatus(subId,
                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING,
                 mDatagramController.getReceivePendingCount(),
-                SatelliteManager.SATELLITE_ERROR_NONE);
+                SatelliteManager.SATELLITE_RESULT_SUCCESS);
         mDatagramTransferStartTime = System.currentTimeMillis();
         Phone phone = SatelliteServiceUtils.getPhone();
 
@@ -641,6 +688,8 @@
                     || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
                 logd("onSatelliteModemStateChanged: cleaning up resources");
                 cleanUpResources();
+            } else if (state == SATELLITE_MODEM_STATE_CONNECTED) {
+                handleSatelliteConnectedEvent();
             }
         }
     }
@@ -649,31 +698,40 @@
     private void cleanupDemoModeResources() {
         if (isSatelliteAlignedTimerStarted()) {
             stopSatelliteAlignedTimer();
-            if (mPollPendingSatelliteDatagramsRequest == null) {
+            if (mDemoPollPendingSatelliteDatagramsRequest == null) {
                 loge("Satellite aligned timer was started "
-                        + "but mPollPendingSatelliteDatagramsRequest is null");
+                        + "but mDemoPollPendingSatelliteDatagramsRequest is null");
             } else {
                 Consumer<Integer> callback =
-                        (Consumer<Integer>) mPollPendingSatelliteDatagramsRequest.argument;
-                callback.accept(SatelliteManager.SATELLITE_REQUEST_ABORTED);
+                        (Consumer<Integer>) mDemoPollPendingSatelliteDatagramsRequest.argument;
+                callback.accept(SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
             }
         }
         mIsDemoMode = false;
-        mPollPendingSatelliteDatagramsRequest = null;
+        mDemoPollPendingSatelliteDatagramsRequest = null;
         mIsAligned = false;
     }
 
     @GuardedBy("mLock")
     private void cleanUpResources() {
+        synchronized (mLock) {
+            if (mPendingPollSatelliteDatagramsRequest != null) {
+                Consumer<Integer> callback =
+                        (Consumer<Integer>) mPendingPollSatelliteDatagramsRequest.argument;
+                callback.accept(SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
+                mPendingPollSatelliteDatagramsRequest = null;
+            }
+            stopDatagramWaitForConnectedStateTimer();
+        }
         if (mDatagramController.isReceivingDatagrams()) {
             mDatagramController.updateReceiveStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
                     mDatagramController.getReceivePendingCount(),
-                    SatelliteManager.SATELLITE_REQUEST_ABORTED);
+                    SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
         }
         mDatagramController.updateReceiveStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0,
-                SatelliteManager.SATELLITE_ERROR_NONE);
+                SatelliteManager.SATELLITE_RESULT_SUCCESS);
         cleanupDemoModeResources();
     }
 
@@ -695,7 +753,7 @@
 
     /** Report incoming datagram related metrics */
     private void reportMetrics(@Nullable SatelliteDatagram satelliteDatagram,
-            @NonNull @SatelliteManager.SatelliteError int resultCode) {
+            @NonNull @SatelliteManager.SatelliteResult int resultCode) {
         int datagramSizeRoundedBytes = -1;
         int datagramTransferTime = 0;
 
@@ -728,7 +786,7 @@
     }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    protected void onDeviceAlignedWithSatellite(boolean isAligned) {
+    protected void setDeviceAlignedWithSatellite(boolean isAligned) {
         if (mIsDemoMode) {
             synchronized (mLock) {
                 mIsAligned = isAligned;
@@ -742,7 +800,7 @@
             logd("Satellite aligned timer was already started");
             return;
         }
-        mPollPendingSatelliteDatagramsRequest = request;
+        mDemoPollPendingSatelliteDatagramsRequest = request;
         sendMessageDelayed(
                 obtainMessage(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT, request),
                 getSatelliteAlignedTimeoutDuration());
@@ -757,13 +815,14 @@
         if (isSatelliteAlignedTimerStarted()) {
             stopSatelliteAlignedTimer();
 
-            if (mPollPendingSatelliteDatagramsRequest == null) {
-                loge("handleSatelliteAlignedTimer: mPollPendingSatelliteDatagramsRequest is null");
+            if (mDemoPollPendingSatelliteDatagramsRequest == null) {
+                loge("handleSatelliteAlignedTimer: mDemoPollPendingSatelliteDatagramsRequest "
+                        + "is null");
             } else {
                 Message message = obtainMessage(
                         EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE,
-                        mPollPendingSatelliteDatagramsRequest);
-                mPollPendingSatelliteDatagramsRequest = null;
+                        mDemoPollPendingSatelliteDatagramsRequest);
+                mDemoPollPendingSatelliteDatagramsRequest = null;
                 AsyncResult.forMessage(message, null, null);
                 message.sendToTarget();
             }
@@ -773,7 +832,7 @@
     private void handleEventSatelliteAlignedTimeout(DatagramReceiverHandlerRequest request) {
         SatelliteManager.SatelliteException exception =
                 new SatelliteManager.SatelliteException(
-                        SatelliteManager.SATELLITE_NOT_REACHABLE);
+                        SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE);
         Message message = obtainMessage(EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE, request);
         AsyncResult.forMessage(message, null, exception);
         message.sendToTarget();
@@ -787,6 +846,42 @@
         removeMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT);
     }
 
+    private void startDatagramWaitForConnectedStateTimer() {
+        if (isDatagramWaitForConnectedStateTimerStarted()) {
+            logd("DatagramWaitForConnectedStateTimer is already started");
+            return;
+        }
+        sendMessageDelayed(obtainMessage(
+                        EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT),
+                mDatagramController.getDatagramWaitTimeForConnectedState());
+    }
+
+    private void stopDatagramWaitForConnectedStateTimer() {
+        removeMessages(EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT);
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public boolean isDatagramWaitForConnectedStateTimerStarted() {
+        return hasMessages(EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT);
+    }
+
+    private void handleEventDatagramWaitForConnectedStateTimedOut() {
+        synchronized (mLock) {
+            if (mPendingPollSatelliteDatagramsRequest == null) {
+                logw("handleEventDatagramWaitForConnectedStateTimedOut: "
+                        + "mPendingPollSatelliteDatagramsRequest is null");
+                return;
+            }
+
+            logw("Timed out to wait for satellite connected before polling datagrams");
+            Consumer<Integer> callback =
+                    (Consumer<Integer>) mPendingPollSatelliteDatagramsRequest.argument;
+            callback.accept(SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE);
+            mPendingPollSatelliteDatagramsRequest = null;
+            SatelliteSessionController.getInstance().onDatagramWaitForConnectedStateTimerTimedOut();
+        }
+    }
+
     /**
      * Destroys this DatagramDispatcher. Used for tearing down static resources during testing.
      */
@@ -802,4 +897,8 @@
     private static void loge(@NonNull String log) {
         Rlog.e(TAG, log);
     }
+
+    private static void logw(@NonNull String log) {
+        Rlog.w(TAG, log);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
index 5d4e5dd..26100a8 100644
--- a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
+++ b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
@@ -40,7 +40,7 @@
     public static void resolveNtnCapability(
             @NonNull NetworkRegistrationInfo networkRegistrationInfo, int subId) {
         SatelliteController satelliteController = SatelliteController.getInstance();
-        List<String> satellitePlmnList = satelliteController.getSatellitePlmnList();
+        List<String> satellitePlmnList = satelliteController.getSatellitePlmnList(subId);
         String registeredPlmn = networkRegistrationInfo.getRegisteredPlmn();
         for (String satellitePlmn : satellitePlmnList) {
             if (TextUtils.equals(satellitePlmn, registeredPlmn)) {
diff --git a/src/java/com/android/internal/telephony/satellite/PointingAppController.java b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
index 9dba6f2..70d432e 100644
--- a/src/java/com/android/internal/telephony/satellite/PointingAppController.java
+++ b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
@@ -16,12 +16,16 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Handler;
@@ -41,6 +45,7 @@
 import com.android.internal.telephony.Phone;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
@@ -57,9 +62,12 @@
     private static PointingAppController sInstance;
     @NonNull private final Context mContext;
     private boolean mStartedSatelliteTransmissionUpdates;
+    private boolean mLastNeedFullScreenPointingUI;
+    private boolean mListenerForPointingUIRegistered;
     @NonNull private String mPointingUiPackageName = "";
     @NonNull private String mPointingUiClassName = "";
-
+    @NonNull private ActivityManager mActivityManager;
+    @NonNull public UidImportanceListener mUidImportanceListener = new UidImportanceListener();
     /**
      * Map key: subId, value: SatelliteTransmissionUpdateHandler to notify registrants.
      */
@@ -97,6 +105,9 @@
     public PointingAppController(@NonNull Context context) {
         mContext = context;
         mStartedSatelliteTransmissionUpdates = false;
+        mLastNeedFullScreenPointingUI = false;
+        mListenerForPointingUIRegistered = false;
+        mActivityManager = mContext.getSystemService(ActivityManager.class);
     }
 
     /**
@@ -119,6 +130,36 @@
         return mStartedSatelliteTransmissionUpdates;
     }
 
+    /**
+     * Get the flag mStartedSatelliteTransmissionUpdates
+     * @return returns mStartedSatelliteTransmissionUpdates
+     */
+    @VisibleForTesting
+    public boolean getLastNeedFullScreenPointingUI() {
+        return mLastNeedFullScreenPointingUI;
+    }
+
+    /**
+     * Listener for handling pointing UI App in the event of crash
+     */
+    @VisibleForTesting
+    public class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
+        @Override
+        public void onUidImportance(int uid, int importance) {
+            if (importance != IMPORTANCE_GONE) return;
+            final PackageManager pm = mContext.getPackageManager();
+            final String[] callerPackages = pm.getPackagesForUid(uid);
+            String pointingUiPackage = getPointingUiPackageName();
+
+            if (callerPackages != null) {
+                if (Arrays.stream(callerPackages).anyMatch(pointingUiPackage::contains)) {
+                    logd("Restarting pointingUI");
+                    startPointingUI(mLastNeedFullScreenPointingUI);
+                }
+            }
+        }
+    }
+
     private static final class DatagramTransferStateHandlerRequest {
         public int datagramTransferState;
         public int pendingCount;
@@ -279,7 +320,7 @@
         if (handler != null) {
             handler.removeListener(callback);
             if (handler.hasListeners()) {
-                result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+                result.accept(SatelliteManager.SATELLITE_RESULT_SUCCESS);
                 return;
             }
 
@@ -291,7 +332,7 @@
                         handler);
             } else {
                 if (phone == null) {
-                    result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                    result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
                     return;
                 }
                 phone.unregisterForSatellitePositionInfoChanged(handler);
@@ -311,7 +352,7 @@
         if (mStartedSatelliteTransmissionUpdates) {
             logd("startSatelliteTransmissionUpdates: already started");
             AsyncResult.forMessage(message, null, new SatelliteManager.SatelliteException(
-                    SatelliteManager.SATELLITE_ERROR_NONE));
+                    SatelliteManager.SATELLITE_RESULT_SUCCESS));
             message.sendToTarget();
             return;
         }
@@ -326,7 +367,7 @@
         } else {
             loge("startSatelliteTransmissionUpdates: No phone object");
             AsyncResult.forMessage(message, null, new SatelliteManager.SatelliteException(
-                    SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE));
+                    SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE));
             message.sendToTarget();
         }
     }
@@ -347,7 +388,7 @@
         } else {
             loge("startSatelliteTransmissionUpdates: No phone object");
             AsyncResult.forMessage(message, null, new SatelliteManager.SatelliteException(
-                    SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE));
+                    SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE));
             message.sendToTarget();
         }
     }
@@ -379,12 +420,28 @@
         launchIntent.putExtra("needFullScreen", needFullScreenPointingUI);
 
         try {
+            if (!mListenerForPointingUIRegistered) {
+                mActivityManager.addOnUidImportanceListener(mUidImportanceListener,
+                        IMPORTANCE_GONE);
+                mListenerForPointingUIRegistered = true;
+            }
+            mLastNeedFullScreenPointingUI = needFullScreenPointingUI;
             mContext.startActivity(launchIntent);
         } catch (ActivityNotFoundException ex) {
             loge("startPointingUI: Pointing UI app activity is not found, ex=" + ex);
         }
     }
 
+    /**
+     * Remove the Importance Listener For Pointing UI App once the satellite is disabled
+     */
+    public void removeListenerForPointingUI() {
+        if (mListenerForPointingUIRegistered) {
+            mActivityManager.removeOnUidImportanceListener(mUidImportanceListener);
+            mListenerForPointingUIRegistered = false;
+        }
+    }
+
     public void updateSendDatagramTransferState(int subId,
             @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
             int sendPendingCount, int errorCode) {
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 957b152..050dfbc 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -16,6 +16,10 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.SubscriptionManager.SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
+import static android.telephony.SubscriptionManager.isValidSubscriptionId;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER;
+
 import android.annotation.ArrayRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -25,7 +29,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.net.wifi.WifiManager;
@@ -68,6 +71,7 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
@@ -75,7 +79,9 @@
 import com.android.internal.util.FunctionalUtils;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -127,6 +133,9 @@
     private static final int EVENT_SATELLITE_PROVISION_STATE_CHANGED = 26;
     private static final int EVENT_PENDING_DATAGRAMS = 27;
     private static final int EVENT_SATELLITE_MODEM_STATE_CHANGED = 28;
+    private static final int EVENT_SET_SATELLITE_PLMN_INFO_DONE = 29;
+    private static final int CMD_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE = 30;
+    private static final int EVENT_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE_DONE = 31;
 
     @NonNull private static SatelliteController sInstance;
     @NonNull private final Context mContext;
@@ -136,7 +145,7 @@
     @NonNull private final DatagramController mDatagramController;
     @NonNull private final ControllerMetricsStats mControllerMetricsStats;
     @NonNull private final ProvisionMetricsStats mProvisionMetricsStats;
-    private SharedPreferences mSharedPreferences = null;
+    @NonNull private final SubscriptionManagerService mSubscriptionManagerService;
     private final CommandsInterface mCi;
     private ContentResolver mContentResolver = null;
 
@@ -225,8 +234,16 @@
     @NonNull private final Object mCarrierConfigArrayLock = new Object();
     @GuardedBy("mCarrierConfigArrayLock")
     @NonNull private final SparseArray<PersistableBundle> mCarrierConfigArray = new SparseArray<>();
-    @NonNull private final List<String> mSatellitePlmnList;
-
+    @GuardedBy("mIsSatelliteEnabledLock")
+    /** Key: Subscription ID, value: set of restriction reasons for satellite communication.*/
+    @NonNull private final Map<Integer, Set<Integer>> mSatelliteAttachRestrictionForCarrierArray =
+            new HashMap<>();
+    @GuardedBy("mIsSatelliteEnabledLock")
+    /** Key: Subscription ID, value: the actual satellite enabled state in the modem -
+     * {@code true} for enabled and {@code false} for disabled. */
+    @NonNull private final Map<Integer, Boolean> mIsSatelliteAttachEnabledForCarrierArrayPerSub =
+            new HashMap<>();
+    @NonNull private final FeatureFlags mFeatureFlags;
     /**
      * @return The singleton instance of SatelliteController.
      */
@@ -240,12 +257,13 @@
     /**
      * Create the SatelliteController singleton instance.
      * @param context The Context to use to create the SatelliteController.
+     * @param featureFlags The feature flag.
      */
-    public static void make(@NonNull Context context) {
+    public static void make(@NonNull Context context, @NonNull FeatureFlags featureFlags) {
         if (sInstance == null) {
             HandlerThread satelliteThread = new HandlerThread(TAG);
             satelliteThread.start();
-            sInstance = new SatelliteController(context, satelliteThread.getLooper());
+            sInstance = new SatelliteController(context, satelliteThread.getLooper(), featureFlags);
         }
     }
 
@@ -255,12 +273,15 @@
      *
      * @param context The Context for the SatelliteController.
      * @param looper The looper for the handler. It does not run on main thread.
+     * @param featureFlags The feature flag.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public SatelliteController(@NonNull Context context, @NonNull Looper looper) {
+    public SatelliteController(
+            @NonNull Context context, @NonNull Looper looper, @NonNull FeatureFlags featureFlags) {
         super(looper);
 
         mContext = context;
+        mFeatureFlags = featureFlags;
         Phone phone = SatelliteServiceUtils.getPhone();
         mCi = phone.mCi;
         // Create the SatelliteModemInterface singleton, which is used to manage connections
@@ -275,6 +296,7 @@
         // should be called before making DatagramController
         mControllerMetricsStats = ControllerMetricsStats.make(mContext);
         mProvisionMetricsStats = ProvisionMetricsStats.getOrCreateInstance();
+        mSubscriptionManagerService = SubscriptionManagerService.getInstance();
 
         // Create the DatagramController singleton,
         // which is used to send and receive satellite datagrams.
@@ -295,13 +317,6 @@
         mContentResolver = mContext.getContentResolver();
         mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
 
-        try {
-            mSharedPreferences = mContext.getSharedPreferences(SATELLITE_SHARED_PREF,
-                    Context.MODE_PRIVATE);
-        } catch (Exception e) {
-            loge("Cannot get default shared preferences: " + e);
-        }
-
         initializeSatelliteModeRadios();
 
         ContentObserver satelliteModeRadiosContentObserver = new ContentObserver(this) {
@@ -317,8 +332,6 @@
         }
 
         mSatelliteServicesSupportedByProviders = readSupportedSatelliteServicesFromOverlayConfig();
-        mSatellitePlmnList =
-                mSatelliteServicesSupportedByProviders.keySet().stream().toList();
         updateSupportedSatelliteServicesForActiveSubscriptions();
         mCarrierConfigChangeListener =
                 (slotIndex, subId, carrierId, specificCarrierId) ->
@@ -541,6 +554,21 @@
         }
     }
 
+    private static final class RequestHandleSatelliteAttachRestrictionForCarrierArgument {
+        public int subId;
+        @SatelliteManager.SatelliteCommunicationRestrictionReason
+        public int reason;
+        @NonNull public Consumer<Integer> callback;
+
+        RequestHandleSatelliteAttachRestrictionForCarrierArgument(int subId,
+                @SatelliteManager.SatelliteCommunicationRestrictionReason int reason,
+                Consumer<Integer> callback) {
+            this.subId = subId;
+            this.reason = reason;
+            this.callback = callback;
+        }
+    }
+
     private static final class ProvisionSatelliteServiceArgument {
         @NonNull public String token;
         @NonNull public byte[] provisionData;
@@ -616,7 +644,7 @@
                         (ProvisionSatelliteServiceArgument) request.argument;
                 if (mSatelliteProvisionCallbacks.containsKey(argument.subId)) {
                     argument.callback.accept(
-                            SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS);
+                            SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS);
                     notifyRequester(request);
                     break;
                 }
@@ -634,13 +662,15 @@
                     phone.provisionSatelliteService(onCompleted, argument.token);
                 } else {
                     loge("provisionSatelliteService: No phone object");
-                    argument.callback.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                    argument.callback.accept(
+                            SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
                     notifyRequester(request);
                     mProvisionMetricsStats
-                            .setResultCode(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE)
+                            .setResultCode(
+                                    SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE)
                             .reportProvisionMetrics();
                     mControllerMetricsStats.reportProvisionCount(
-                            SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                            SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
                 }
                 break;
             }
@@ -676,12 +706,12 @@
                     loge("deprovisionSatelliteService: No phone object");
                     if (argument.callback != null) {
                         argument.callback.accept(
-                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
-                        mProvisionMetricsStats
-                                .setResultCode(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE)
+                                SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
+                        mProvisionMetricsStats.setResultCode(
+                                SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE)
                                 .reportProvisionMetrics();
                         mControllerMetricsStats.reportDeprovisionCount(
-                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                                SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
                     }
                 }
                 break;
@@ -711,7 +741,7 @@
                 int error =  SatelliteServiceUtils.getSatelliteError(ar, "setSatelliteEnabled");
                 logd("EVENT_SET_SATELLITE_ENABLED_DONE = " + error);
 
-                if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     if (argument.enableSatellite) {
                         synchronized (mSatelliteEnabledRequestLock) {
                             mWaitingForRadioDisabled = true;
@@ -726,6 +756,13 @@
                         }
                         evaluateToSendSatelliteEnabledSuccess();
                     } else {
+                        /**
+                         * Unregister Importance Listener for Pointing UI
+                         * when Satellite is disabled
+                         */
+                        if (mNeedsSatellitePointing) {
+                            mPointingAppController.removeListenerForPointingUI();
+                        }
                         synchronized (mSatelliteEnabledRequestLock) {
                             if (mSatelliteEnabledRequest != null &&
                                     mSatelliteEnabledRequest.enableSatellite == true &&
@@ -733,14 +770,15 @@
                                 // Previous mSatelliteEnabledRequest is successful but waiting for
                                 // all radios to be turned off.
                                 mSatelliteEnabledRequest.callback.accept(
-                                        SatelliteManager.SATELLITE_ERROR_NONE);
+                                        SatelliteManager.SATELLITE_RESULT_SUCCESS);
                             }
                         }
 
                         synchronized (mIsSatelliteEnabledLock) {
                             if (!mWaitingForSatelliteModemOff) {
                                 moveSatelliteToOffStateAndCleanUpResources(
-                                        SatelliteManager.SATELLITE_ERROR_NONE, argument.callback);
+                                        SatelliteManager.SATELLITE_RESULT_SUCCESS,
+                                        argument.callback);
                             } else {
                                 logd("Wait for satellite modem off before updating satellite"
                                         + " modem state");
@@ -756,7 +794,7 @@
                             // Previous mSatelliteEnabledRequest is successful but waiting for
                             // all radios to be turned off.
                             mSatelliteEnabledRequest.callback.accept(
-                                    SatelliteManager.SATELLITE_ERROR_NONE);
+                                    SatelliteManager.SATELLITE_RESULT_SUCCESS);
                         }
                     }
                     resetSatelliteEnabledRequest();
@@ -766,7 +804,7 @@
                 }
 
                 if (argument.enableSatellite) {
-                    if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                    if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                         mControllerMetricsStats.onSatelliteEnabled();
                         mControllerMetricsStats.reportServiceEnablementSuccessCount();
                     } else {
@@ -798,7 +836,7 @@
                 } else {
                     loge("isSatelliteEnabled: No phone object");
                     ((ResultReceiver) request.argument).send(
-                            SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+                            SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
                 }
                 break;
             }
@@ -809,17 +847,17 @@
                 int error =  SatelliteServiceUtils.getSatelliteError(ar,
                         "isSatelliteEnabled");
                 Bundle bundle = new Bundle();
-                if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     if (ar.result == null) {
                         loge("isSatelliteEnabled: result is null");
-                        error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+                        error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         boolean enabled = ((int[]) ar.result)[0] == 1;
                         if (DBG) logd("isSatelliteEnabled: " + enabled);
                         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_ENABLED, enabled);
                         updateSatelliteEnabledState(enabled, "EVENT_IS_SATELLITE_ENABLED_DONE");
                     }
-                } else if (error == SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED) {
+                } else if (error == SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED) {
                     updateSatelliteSupportedStateWhenSatelliteServiceConnected(false);
                 }
                 ((ResultReceiver) request.argument).send(error, bundle);
@@ -840,7 +878,7 @@
                 } else {
                     loge("isSatelliteSupported: No phone object");
                     ((ResultReceiver) request.argument).send(
-                            SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+                            SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
                 }
                 break;
             }
@@ -850,10 +888,10 @@
                 request = (SatelliteControllerHandlerRequest) ar.userObj;
                 int error =  SatelliteServiceUtils.getSatelliteError(ar, "isSatelliteSupported");
                 Bundle bundle = new Bundle();
-                if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     if (ar.result == null) {
                         loge("isSatelliteSupported: result is null");
-                        error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+                        error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         boolean supported = (boolean) ar.result;
                         if (DBG) logd("isSatelliteSupported: " + supported);
@@ -878,7 +916,7 @@
                 } else {
                     loge("getSatelliteCapabilities: No phone object");
                     ((ResultReceiver) request.argument).send(
-                            SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+                            SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
                 }
                 break;
             }
@@ -889,10 +927,10 @@
                 int error =  SatelliteServiceUtils.getSatelliteError(ar,
                         "getSatelliteCapabilities");
                 Bundle bundle = new Bundle();
-                if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     if (ar.result == null) {
                         loge("getSatelliteCapabilities: result is null");
-                        error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+                        error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         SatelliteCapabilities capabilities = (SatelliteCapabilities) ar.result;
                         synchronized (mNeedsSatellitePointingLock) {
@@ -926,7 +964,7 @@
                 } else {
                     loge("isSatelliteCommunicationAllowedForCurrentLocation: No phone object");
                     ((ResultReceiver) request.argument).send(
-                            SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+                            SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
                 }
                 break;
             }
@@ -937,10 +975,10 @@
                 int error =  SatelliteServiceUtils.getSatelliteError(ar,
                         "isSatelliteCommunicationAllowedForCurrentLocation");
                 Bundle bundle = new Bundle();
-                if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     if (ar.result == null) {
                         loge("isSatelliteCommunicationAllowedForCurrentLocation: result is null");
-                        error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+                        error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         boolean communicationAllowed = (boolean) ar.result;
                         if (DBG) {
@@ -970,7 +1008,7 @@
                 } else {
                     loge("requestTimeForNextSatelliteVisibility: No phone object");
                     ((ResultReceiver) request.argument).send(
-                            SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+                            SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
                 }
                 break;
             }
@@ -981,10 +1019,10 @@
                 int error = SatelliteServiceUtils.getSatelliteError(ar,
                         "requestTimeForNextSatelliteVisibility");
                 Bundle bundle = new Bundle();
-                if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     if (ar.result == null) {
                         loge("requestTimeForNextSatelliteVisibility: result is null");
-                        error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+                        error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         int nextVisibilityDuration = ((int[]) ar.result)[0];
                         if (DBG) {
@@ -1039,9 +1077,6 @@
                                         SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, receiver);
                             }
                         }
-                    } else {
-                        logd("EVENT_RADIO_STATE_CHANGED: Satellite vendor service is supported."
-                                + " Ignored the event");
                     }
                 }
                 break;
@@ -1060,7 +1095,7 @@
                 } else {
                     loge("isSatelliteProvisioned: No phone object");
                     ((ResultReceiver) request.argument).send(
-                            SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+                            SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
                 }
                 break;
             }
@@ -1071,10 +1106,10 @@
                 int error =  SatelliteServiceUtils.getSatelliteError(ar,
                         "isSatelliteProvisioned");
                 Bundle bundle = new Bundle();
-                if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     if (ar.result == null) {
                         loge("isSatelliteProvisioned: result is null");
-                        error = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+                        error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         boolean provisioned = ((int[]) ar.result)[0] == 1;
                         if (DBG) logd("isSatelliteProvisioned: " + provisioned);
@@ -1118,6 +1153,41 @@
                 }
                 break;
 
+            case EVENT_SET_SATELLITE_PLMN_INFO_DONE:
+                handleSetSatellitePlmnInfoDoneEvent(msg);
+                break;
+
+            case CMD_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE: {
+                logd("CMD_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE");
+                request = (SatelliteControllerHandlerRequest) msg.obj;
+                handleRequestSatelliteAttachRestrictionForCarrierCmd(request);
+                break;
+            }
+
+            case EVENT_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE_DONE: {
+                ar = (AsyncResult) msg.obj;
+                request = (SatelliteControllerHandlerRequest) ar.userObj;
+                RequestHandleSatelliteAttachRestrictionForCarrierArgument argument =
+                        (RequestHandleSatelliteAttachRestrictionForCarrierArgument)
+                                request.argument;
+                int subId = argument.subId;
+                int error =  SatelliteServiceUtils.getSatelliteError(ar,
+                        "requestSetSatelliteEnabledForCarrier");
+
+                synchronized (mIsSatelliteEnabledLock) {
+                    if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
+                        boolean enableSatellite = mSatelliteAttachRestrictionForCarrierArray
+                                .getOrDefault(argument.subId, Collections.emptySet()).isEmpty();
+                        mIsSatelliteAttachEnabledForCarrierArrayPerSub.put(subId, enableSatellite);
+                    } else {
+                        mIsSatelliteAttachEnabledForCarrierArrayPerSub.remove(subId);
+                    }
+                }
+
+                argument.callback.accept(error);
+                break;
+            }
+
             default:
                 Log.w(TAG, "SatelliteControllerHandler: unexpected message code: " +
                         msg.what);
@@ -1151,28 +1221,28 @@
 
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         if (!satelliteSupported) {
-            result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            result.accept(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED);
             return;
         }
 
         Boolean satelliteProvisioned = isSatelliteProvisioned();
         if (satelliteProvisioned == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         if (!satelliteProvisioned) {
-            result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+            result.accept(SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED);
             return;
         }
 
         if (enableSatellite) {
             if (!mIsRadioOn) {
                 loge("Radio is not on, can not enable satellite");
-                result.accept(SatelliteManager.SATELLITE_INVALID_MODEM_STATE);
+                result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE);
                 return;
             }
         } else {
@@ -1186,12 +1256,12 @@
                     if (enableDemoMode != mIsDemoModeEnabled) {
                         loge("Received invalid demo mode while satellite session is enabled"
                                 + " enableDemoMode = " + enableDemoMode);
-                        result.accept(SatelliteManager.SATELLITE_INVALID_ARGUMENTS);
+                        result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS);
                         return;
                     } else {
                         logd("Enable request matches with current state"
                                 + " enableSatellite = " + enableSatellite);
-                        result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+                        result.accept(SatelliteManager.SATELLITE_RESULT_SUCCESS);
                         return;
                     }
                 }
@@ -1206,7 +1276,8 @@
          * 2. If there is a ongoing request, then:
          *      1. ongoing request = enable, current request = enable: return IN_PROGRESS error
          *      2. ongoing request = disable, current request = disable: return IN_PROGRESS error
-         *      3. ongoing request = disable, current request = enable: return SATELLITE_ERROR error
+         *      3. ongoing request = disable, current request = enable: return
+         *      SATELLITE_RESULT_ERROR error
          *      4. ongoing request = enable, current request = disable: send request to modem
          */
         synchronized (mSatelliteEnabledRequestLock) {
@@ -1215,13 +1286,13 @@
             } else if (mSatelliteEnabledRequest.enableSatellite == request.enableSatellite) {
                 logd("requestSatelliteEnabled enableSatellite: " + enableSatellite
                         + " is already in progress.");
-                result.accept(SatelliteManager.SATELLITE_REQUEST_IN_PROGRESS);
+                result.accept(SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS);
                 return;
             } else if (mSatelliteEnabledRequest.enableSatellite == false
                     && request.enableSatellite == true) {
                 logd("requestSatelliteEnabled enableSatellite: " + enableSatellite + " cannot be "
                         + "processed. Disable satellite is already in progress.");
-                result.accept(SatelliteManager.SATELLITE_ERROR);
+                result.accept(SatelliteManager.SATELLITE_RESULT_ERROR);
                 return;
             }
         }
@@ -1239,11 +1310,11 @@
     public void requestIsSatelliteEnabled(int subId, @NonNull ResultReceiver result) {
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
             return;
         }
         if (!satelliteSupported) {
-            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
             return;
         }
 
@@ -1252,7 +1323,7 @@
                 /* We have already successfully queried the satellite modem. */
                 Bundle bundle = new Bundle();
                 bundle.putBoolean(SatelliteManager.KEY_SATELLITE_ENABLED, mIsSatelliteEnabled);
-                result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+                result.send(SatelliteManager.SATELLITE_RESULT_SUCCESS, bundle);
                 return;
             }
         }
@@ -1282,27 +1353,27 @@
     public void requestIsDemoModeEnabled(int subId, @NonNull ResultReceiver result) {
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
             return;
         }
         if (!satelliteSupported) {
-            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
             return;
         }
 
         Boolean satelliteProvisioned = isSatelliteProvisioned();
         if (satelliteProvisioned == null) {
-            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
             return;
         }
         if (!satelliteProvisioned) {
-            result.send(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED, null);
             return;
         }
 
         final Bundle bundle = new Bundle();
         bundle.putBoolean(SatelliteManager.KEY_DEMO_MODE_ENABLED, mIsDemoModeEnabled);
-        result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+        result.send(SatelliteManager.SATELLITE_RESULT_SUCCESS, bundle);
     }
 
     /**
@@ -1327,7 +1398,7 @@
                 /* We have already successfully queried the satellite modem. */
                 Bundle bundle = new Bundle();
                 bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, mIsSatelliteSupported);
-                result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+                result.send(SatelliteManager.SATELLITE_RESULT_SUCCESS, bundle);
                 return;
             }
         }
@@ -1345,11 +1416,11 @@
     public void requestSatelliteCapabilities(int subId, @NonNull ResultReceiver result) {
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
             return;
         }
         if (!satelliteSupported) {
-            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
             return;
         }
 
@@ -1358,7 +1429,7 @@
                 Bundle bundle = new Bundle();
                 bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES,
                         mSatelliteCapabilities);
-                result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+                result.send(SatelliteManager.SATELLITE_RESULT_SUCCESS, bundle);
                 return;
             }
         }
@@ -1381,21 +1452,21 @@
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         if (!satelliteSupported) {
-            result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            result.accept(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED);
             return;
         }
 
         Boolean satelliteProvisioned = isSatelliteProvisioned();
         if (satelliteProvisioned == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         if (!satelliteProvisioned) {
-            result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+            result.accept(SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED);
             return;
         }
 
@@ -1420,21 +1491,21 @@
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         if (!satelliteSupported) {
-            result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            result.accept(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED);
             return;
         }
 
         Boolean satelliteProvisioned = isSatelliteProvisioned();
         if (satelliteProvisioned == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         if (!satelliteProvisioned) {
-            result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+            result.accept(SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED);
             return;
         }
 
@@ -1468,23 +1539,23 @@
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return null;
         }
         if (!satelliteSupported) {
-            result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            result.accept(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED);
             return null;
         }
 
         final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
         if (mSatelliteProvisionCallbacks.containsKey(validSubId)) {
-            result.accept(SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS);
+            result.accept(SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS);
             return null;
         }
 
         Boolean satelliteProvisioned = isSatelliteProvisioned();
         if (satelliteProvisioned != null && satelliteProvisioned) {
-            result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_SUCCESS);
             return null;
         }
 
@@ -1519,21 +1590,21 @@
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         if (!satelliteSupported) {
-            result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            result.accept(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED);
             return;
         }
 
         Boolean satelliteProvisioned = isSatelliteProvisioned();
         if (satelliteProvisioned == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         if (!satelliteProvisioned) {
-            result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_SUCCESS);
             return;
         }
 
@@ -1550,20 +1621,20 @@
      * @param subId The subId of the subscription to register for provision state changed.
      * @param callback The callback to handle the satellite provision state changed event.
      *
-     * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+     * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
-    @SatelliteManager.SatelliteError public int registerForSatelliteProvisionStateChanged(int subId,
-            @NonNull ISatelliteProvisionStateCallback callback) {
+    @SatelliteManager.SatelliteResult public int registerForSatelliteProvisionStateChanged(
+            int subId, @NonNull ISatelliteProvisionStateCallback callback) {
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+            return SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
         }
         if (!satelliteSupported) {
-            return SatelliteManager.SATELLITE_NOT_SUPPORTED;
+            return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
 
         mSatelliteProvisionStateChangedListeners.put(callback.asBinder(), callback);
-        return SatelliteManager.SATELLITE_ERROR_NONE;
+        return SatelliteManager.SATELLITE_RESULT_SUCCESS;
     }
 
     /**
@@ -1590,11 +1661,11 @@
     public void requestIsSatelliteProvisioned(int subId, @NonNull ResultReceiver result) {
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
             return;
         }
         if (!satelliteSupported) {
-            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
             return;
         }
 
@@ -1603,7 +1674,7 @@
                 Bundle bundle = new Bundle();
                 bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED,
                         mIsSatelliteProvisioned);
-                result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+                result.send(SatelliteManager.SATELLITE_RESULT_SUCCESS, bundle);
                 return;
             }
         }
@@ -1617,18 +1688,18 @@
      * @param subId The subId of the subscription to register for satellite modem state changed.
      * @param callback The callback to handle the satellite modem state changed event.
      *
-     * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+     * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
-    @SatelliteManager.SatelliteError public int registerForSatelliteModemStateChanged(int subId,
+    @SatelliteManager.SatelliteResult public int registerForSatelliteModemStateChanged(int subId,
             @NonNull ISatelliteStateCallback callback) {
         if (mSatelliteSessionController != null) {
             mSatelliteSessionController.registerForSatelliteModemStateChanged(callback);
         } else {
             loge("registerForSatelliteModemStateChanged: mSatelliteSessionController"
                     + " is not initialized yet");
-            return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+            return SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
         }
-        return SatelliteManager.SATELLITE_ERROR_NONE;
+        return SatelliteManager.SATELLITE_RESULT_SUCCESS;
     }
 
     /**
@@ -1655,9 +1726,9 @@
      * @param subId The subId of the subscription to register for incoming satellite datagrams.
      * @param callback The callback to handle incoming datagrams over satellite.
      *
-     * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+     * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
-    @SatelliteManager.SatelliteError public int registerForSatelliteDatagram(int subId,
+    @SatelliteManager.SatelliteResult public int registerForSatelliteDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
         return mDatagramController.registerForSatelliteDatagram(subId, callback);
     }
@@ -1684,18 +1755,18 @@
      * long, SatelliteDatagram, int, Consumer)}
      *
      * @param subId The subId of the subscription used for receiving datagrams.
-     * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+     * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
     public void pollPendingSatelliteDatagrams(int subId, @NonNull IIntegerConsumer callback) {
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
 
         Boolean satelliteProvisioned = isSatelliteProvisioned();
         if (satelliteProvisioned == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         if (!satelliteProvisioned) {
-            result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+            result.accept(SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED);
             return;
         }
 
@@ -1717,7 +1788,7 @@
      *                 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 callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
+     * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
     public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
             SatelliteDatagram datagram, boolean needFullScreenPointingUI,
@@ -1726,11 +1797,11 @@
 
         Boolean satelliteProvisioned = isSatelliteProvisioned();
         if (satelliteProvisioned == null) {
-            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         if (!satelliteProvisioned) {
-            result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+            result.accept(SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED);
             return;
         }
 
@@ -1759,11 +1830,11 @@
             @NonNull ResultReceiver result) {
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
             return;
         }
         if (!satelliteSupported) {
-            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
             return;
         }
 
@@ -1781,21 +1852,21 @@
     public void requestTimeForNextSatelliteVisibility(int subId, @NonNull ResultReceiver result) {
         Boolean satelliteSupported = isSatelliteSupportedInternal();
         if (satelliteSupported == null) {
-            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
             return;
         }
         if (!satelliteSupported) {
-            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
             return;
         }
 
         Boolean satelliteProvisioned = isSatelliteProvisioned();
         if (satelliteProvisioned == null) {
-            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
             return;
         }
         if (!satelliteProvisioned) {
-            result.send(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED, null);
+            result.send(SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED, null);
             return;
         }
 
@@ -1809,8 +1880,91 @@
      * @param subId The subId of the subscription.
      * @param isAligned {@true} means device is aligned with the satellite, otherwise {@false}.
      */
-    public void onDeviceAlignedWithSatellite(@NonNull int subId, @NonNull boolean isAligned) {
-        mDatagramController.onDeviceAlignedWithSatellite(isAligned);
+    public void setDeviceAlignedWithSatellite(@NonNull int subId, @NonNull boolean isAligned) {
+        mDatagramController.setDeviceAlignedWithSatellite(isAligned);
+    }
+
+    /**
+     * Add a restriction reason for disallowing carrier supported satellite plmn scan and attach
+     * by modem. After updating restriction list, evaluate if satellite should be enabled/disabled,
+     * and request modem to enable/disable satellite accordingly if the desired state does not match
+     * the current state.
+     *
+     * @param subId The subId of the subscription to request for.
+     * @param reason Reason for disallowing satellite communication for carrier.
+     * @param callback The callback to get the result of the request.
+     */
+    public void addSatelliteAttachRestrictionForCarrier(int subId,
+            @SatelliteManager.SatelliteCommunicationRestrictionReason int reason,
+            @NonNull IIntegerConsumer callback) {
+        if (DBG) logd("addSatelliteAttachRestrictionForCarrier(" + subId + ", " + reason + ")");
+        Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+
+        synchronized (mIsSatelliteEnabledLock) {
+            if (mSatelliteAttachRestrictionForCarrierArray.getOrDefault(
+                    subId, Collections.emptySet()).isEmpty()) {
+                mSatelliteAttachRestrictionForCarrierArray.put(subId, new HashSet<>());
+            } else if (mSatelliteAttachRestrictionForCarrierArray.get(subId).contains(reason)) {
+                result.accept(SatelliteManager.SATELLITE_RESULT_SUCCESS);
+                return;
+            }
+            mSatelliteAttachRestrictionForCarrierArray.get(subId).add(reason);
+        }
+        RequestHandleSatelliteAttachRestrictionForCarrierArgument request =
+                new RequestHandleSatelliteAttachRestrictionForCarrierArgument(subId, reason,
+                        result);
+        sendRequestAsync(CMD_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE, request,
+                SatelliteServiceUtils.getPhone(subId));
+    }
+
+    /**
+     * Remove a restriction reason for disallowing carrier supported satellite plmn scan and attach
+     * by modem. After updating restriction list, evaluate if satellite should be enabled/disabled,
+     * and request modem to enable/disable satellite accordingly if the desired state does not match
+     * the current state.
+     *
+     * @param subId The subId of the subscription to request for.
+     * @param reason Reason for disallowing satellite communication.
+     * @param callback The callback to get the result of the request.
+     */
+    public void removeSatelliteAttachRestrictionForCarrier(int subId,
+            @SatelliteManager.SatelliteCommunicationRestrictionReason int reason,
+            @NonNull IIntegerConsumer callback) {
+        Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+        if (DBG) logd("removeSatelliteAttachRestrictionForCarrier(" + subId + ", " + reason + ")");
+        synchronized (mIsSatelliteEnabledLock) {
+            if (mSatelliteAttachRestrictionForCarrierArray.getOrDefault(
+                    subId, Collections.emptySet()).isEmpty()
+                    || !mSatelliteAttachRestrictionForCarrierArray.get(subId).contains(reason)) {
+                result.accept(SatelliteManager.SATELLITE_RESULT_SUCCESS);
+                return;
+            }
+            mSatelliteAttachRestrictionForCarrierArray.get(subId).remove(reason);
+        }
+        RequestHandleSatelliteAttachRestrictionForCarrierArgument request =
+                new RequestHandleSatelliteAttachRestrictionForCarrierArgument(subId, reason,
+                        result);
+        sendRequestAsync(CMD_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE, request,
+                SatelliteServiceUtils.getPhone(subId));
+    }
+
+    /**
+     * Get reasons for disallowing satellite communication, as requested by
+     * {@link #addSatelliteAttachRestrictionForCarrier(int, int, IIntegerConsumer)}.
+     *
+     * @param subId The subId of the subscription to request for.
+     *
+     * @return Set of reasons for disallowing satellite attach for carrier.
+     */
+    public @NonNull Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier(int subId) {
+        synchronized (mIsSatelliteEnabledLock) {
+            Set<Integer> resultSet =
+                    mSatelliteAttachRestrictionForCarrierArray.get(subId);
+            if (resultSet == null) {
+                return new HashSet<>();
+            }
+            return new HashSet<>(resultSet);
+        }
     }
 
     /**
@@ -1940,16 +2094,23 @@
     }
 
     /**
+     * @param subId Subscription ID.
      * @return The list of satellite PLMNs used for connecting to satellite networks.
      */
     @NonNull
-    public List<String> getSatellitePlmnList() {
-        return new ArrayList<>(mSatellitePlmnList);
+    public List<String> getSatellitePlmnList(int subId) {
+        synchronized (mSupportedSatelliteServicesLock) {
+            if (mSupportedSatelliteServices.containsKey(subId)) {
+                return new ArrayList<>(mSupportedSatelliteServices.get(subId).keySet());
+            } else {
+                return new ArrayList<>();
+            }
+        }
     }
 
     /**
      * @param subId Subscription ID.
-     * @param plmn The satellite roaming plmn.
+     * @param plmn The satellite plmn.
      * @return The list of services supported by the carrier associated with the {@code subId} for
      * the satellite network {@code plmn}.
      */
@@ -1974,6 +2135,30 @@
     }
 
     /**
+     * Check whether satellite modem has to attach to a satellite network before sending/receiving
+     * datagrams.
+     *
+     * @return {@code true} if satellite attach is required, {@code false} otherwise.
+     */
+    public boolean isSatelliteAttachRequired() {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            return false;
+        }
+
+        synchronized (mSatelliteCapabilitiesLock) {
+            if (mSatelliteCapabilities == null) {
+                loge("isSatelliteAttachRequired: mSatelliteCapabilities is null");
+                return false;
+            }
+            if (mSatelliteCapabilities.getSupportedRadioTechnologies().contains(
+                    SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN)) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
      * If we have not successfully queried the satellite modem for its satellite service support,
      * we will retry the query one more time. Otherwise, we will return the cached result.
      */
@@ -2000,7 +2185,7 @@
 
     private void handleEventProvisionSatelliteServiceDone(
             @NonNull ProvisionSatelliteServiceArgument arg,
-            @SatelliteManager.SatelliteError int result) {
+            @SatelliteManager.SatelliteResult int result) {
         logd("handleEventProvisionSatelliteServiceDone: result="
                 + result + ", subId=" + arg.subId);
 
@@ -2009,11 +2194,11 @@
             loge("handleEventProvisionSatelliteServiceDone: callback is null for subId="
                     + arg.subId);
             mProvisionMetricsStats
-                    .setResultCode(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE)
+                    .setResultCode(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE)
                     .setIsProvisionRequest(true)
                     .reportProvisionMetrics();
             mControllerMetricsStats.reportProvisionCount(
-                    SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                    SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
         callback.accept(result);
@@ -2021,7 +2206,7 @@
 
     private void handleEventDeprovisionSatelliteServiceDone(
             @NonNull ProvisionSatelliteServiceArgument arg,
-            @SatelliteManager.SatelliteError int result) {
+            @SatelliteManager.SatelliteResult int result) {
         if (arg == null) {
             loge("handleEventDeprovisionSatelliteServiceDone: arg is null");
             return;
@@ -2046,7 +2231,7 @@
                 "handleStartSatelliteTransmissionUpdatesDone");
         arg.errorCallback.accept(errorCode);
 
-        if (errorCode != SatelliteManager.SATELLITE_ERROR_NONE) {
+        if (errorCode != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
             mPointingAppController.setStartedSatelliteTransmissionUpdates(false);
             // We need to remove the callback from our listener list since the caller might not call
             // stopSatelliteTransmissionUpdates to unregister the callback in case of failure.
@@ -2149,10 +2334,25 @@
             phone.setSatellitePower(onCompleted, argument.enableSatellite);
         } else {
             loge("requestSatelliteEnabled: No phone object");
-            argument.callback.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            argument.callback.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
         }
     }
 
+    private void handleRequestSatelliteAttachRestrictionForCarrierCmd(
+            SatelliteControllerHandlerRequest request) {
+        RequestHandleSatelliteAttachRestrictionForCarrierArgument argument =
+                (RequestHandleSatelliteAttachRestrictionForCarrierArgument) request.argument;
+
+        if (argument.reason == SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER) {
+            if (!persistSatelliteAttachEnabledForCarrierSetting(argument.subId)) {
+                argument.callback.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
+                return;
+            }
+        }
+
+        evaluateEnablingSatelliteForCarrier(argument.subId, argument.reason, argument.callback);
+    }
+
     private void updateSatelliteSupportedStateWhenSatelliteServiceConnected(boolean supported) {
         synchronized (mIsSatelliteSupportedLock) {
             mIsSatelliteSupported = supported;
@@ -2290,8 +2490,8 @@
                         || ((mIsSatelliteEnabled == null || isSatelliteEnabled())
                         && !mWaitingForDisableSatelliteModemResponse)) {
                     int error = (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF)
-                            ? SatelliteManager.SATELLITE_ERROR_NONE
-                            : SatelliteManager.SATELLITE_INVALID_MODEM_STATE;
+                            ? SatelliteManager.SATELLITE_RESULT_SUCCESS
+                            : SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE;
                     Consumer<Integer> callback = null;
                     synchronized (mSatelliteEnabledRequestLock) {
                         if (mSatelliteEnabledRequest != null) {
@@ -2307,6 +2507,12 @@
                 }
                 mWaitingForSatelliteModemOff = false;
             }
+        } else {
+            if (mSatelliteSessionController != null) {
+                mSatelliteSessionController.onSatelliteModemStateChanged(state);
+            } else {
+                loge("handleEventSatelliteModemStateChanged: mSatelliteSessionController is null");
+            }
         }
         mDatagramController.onSatelliteModemStateChanged(state);
     }
@@ -2343,7 +2549,7 @@
                 synchronized (mIsSatelliteEnabledLock) {
                     mIsSatelliteEnabled = mSatelliteEnabledRequest.enableSatellite;
                 }
-                mSatelliteEnabledRequest.callback.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+                mSatelliteEnabledRequest.callback.accept(SatelliteManager.SATELLITE_RESULT_SUCCESS);
                 updateSatelliteEnabledState(
                         mSatelliteEnabledRequest.enableSatellite,
                         "EVENT_SET_SATELLITE_ENABLED_DONE");
@@ -2362,7 +2568,7 @@
     }
 
     private void moveSatelliteToOffStateAndCleanUpResources(
-            @SatelliteManager.SatelliteError int error, @Nullable Consumer<Integer> callback) {
+            @SatelliteManager.SatelliteResult int error, @Nullable Consumer<Integer> callback) {
         logd("moveSatelliteToOffStateAndCleanUpResources");
         synchronized (mIsSatelliteEnabledLock) {
             resetSatelliteEnabledRequest();
@@ -2384,10 +2590,25 @@
         return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
     }
 
+    private void configureSatellitePlmnForCarrier(int subId) {
+        logd("configureSatellitePlmnForCarrier()");
+        List<String> satellitePlmnList = getSatellitePlmnList(subId);
+        if (!satellitePlmnList.isEmpty()) {
+            int slotId = SubscriptionManager.getSlotIndex(subId);
+            mSatelliteModemInterface.setSatellitePlmn(slotId, satellitePlmnList,
+                    obtainMessage(EVENT_SET_SATELLITE_PLMN_INFO_DONE));
+        }
+    }
+
+    private void handleSetSatellitePlmnInfoDoneEvent(Message msg) {
+        AsyncResult ar = (AsyncResult) msg.obj;
+        SatelliteServiceUtils.getSatelliteError(ar, "handleSetSatellitePlmnInfoCmd");
+    }
+
     private void updateSupportedSatelliteServicesForActiveSubscriptions() {
         synchronized (mSupportedSatelliteServicesLock) {
             mSupportedSatelliteServices.clear();
-            int[] activeSubIds = SubscriptionManagerService.getInstance().getActiveSubIdList(true);
+            int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(true);
             if (activeSubIds != null) {
                 for (int subId : activeSubIds) {
                     updateSupportedSatelliteServices(subId);
@@ -2434,7 +2655,8 @@
     @NonNull private PersistableBundle getConfigForSubId(int subId) {
         PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId,
                 CarrierConfigManager
-                        .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE);
+                        .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL);
         if (config == null || config.isEmpty()) {
             config = CarrierConfigManager.getDefaultConfig();
         }
@@ -2452,14 +2674,40 @@
 
         updateCarrierConfig(subId);
         updateSupportedSatelliteServicesForActiveSubscriptions();
+        configureSatellitePlmnForCarrier(subId);
+
+        synchronized (mIsSatelliteEnabledLock) {
+            mSatelliteAttachRestrictionForCarrierArray.clear();
+            mIsSatelliteAttachEnabledForCarrierArrayPerSub.clear();
+        }
+
+        setSatelliteAttachEnabledForCarrierOnSimLoaded(subId);
     }
 
-    private void updateCarrierConfig(int subId) {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected void updateCarrierConfig(int subId) {
         synchronized (mCarrierConfigArrayLock) {
             mCarrierConfigArray.put(subId, getConfigForSubId(subId));
         }
     }
 
+    /**
+     * When a SIM is loaded, we need to check if users has enabled satellite attach for the carrier
+     * associated with the SIM, and evaluate if satellite should be enabled for the carrier.
+     *
+     * @param subId Subscription ID.
+     */
+    private void setSatelliteAttachEnabledForCarrierOnSimLoaded(int subId) {
+        synchronized (mIsSatelliteEnabledLock) {
+            if (isSatelliteAttachEnabledForCarrierByUser(subId)
+                    && !mIsSatelliteAttachEnabledForCarrierArrayPerSub.getOrDefault(subId,
+                    false)) {
+                evaluateEnablingSatelliteForCarrier(subId,
+                        SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, null);
+            }
+        }
+    }
+
     @NonNull
     private String[] readStringArrayFromOverlayConfig(@ArrayRes int id) {
         String[] strArray = null;
@@ -2474,6 +2722,188 @@
         return strArray;
     }
 
+    private boolean isSatelliteSupportedForCarrier(int subId) {
+        return getConfigForSubId(subId)
+                .getBoolean(CarrierConfigManager
+                        .KEY_SATELLITE_ATTACH_SUPPORTED_BOOL);
+    }
+
+    /**
+     * Check if satellite attach is enabled by user for the carrier associated with the
+     * {@code subId}.
+     *
+     * @param subId Subscription ID.
+     *
+     * @return Returns {@code true} if satellite attach for carrier is enabled by user,
+     * {@code false} otherwise.
+     */
+    private boolean isSatelliteAttachEnabledForCarrierByUser(int subId) {
+        synchronized (mIsSatelliteEnabledLock) {
+            Set<Integer> cachedRestrictionSet =
+                    mSatelliteAttachRestrictionForCarrierArray.get(subId);
+            if (cachedRestrictionSet != null) {
+                return !cachedRestrictionSet.contains(
+                        SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER);
+            } else {
+                logd("isSatelliteAttachEnabledForCarrierByUser() no correspondent cache, "
+                        + "load from persist storage");
+                try {
+                    String enabled =
+                            mSubscriptionManagerService.getSubscriptionProperty(subId,
+                                    SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
+                                    mContext.getOpPackageName(), mContext.getAttributionTag());
+
+                    if (enabled == null) {
+                        loge("isSatelliteAttachEnabledForCarrierByUser: invalid subId, subId="
+                                + subId);
+                        return false;
+                    }
+
+                    if (enabled.isEmpty()) {
+                        loge("isSatelliteAttachEnabledForCarrierByUser: no data for subId(" + subId
+                                + ")");
+                        return false;
+                    }
+
+                    synchronized (mIsSatelliteEnabledLock) {
+                        boolean result = enabled.equals("1");
+                        if (!result) {
+                            mSatelliteAttachRestrictionForCarrierArray.put(subId, new HashSet<>());
+                            mSatelliteAttachRestrictionForCarrierArray.get(subId).add(
+                                    SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER);
+                        }
+                        return result;
+                    }
+                } catch (IllegalArgumentException | SecurityException ex) {
+                    loge("isSatelliteAttachEnabledForCarrierByUser: ex=" + ex);
+                    return false;
+                }
+            }
+        }
+    }
+
+    /**
+     * Check whether there is any reason to restrict satellite communication for the carrier
+     * associated with the {@code subId}.
+     *
+     * @param subId Subscription ID
+     * @return {@code true} when there is at least on reason, {@code false} otherwise.
+     */
+    private boolean hasReasonToRestrictSatelliteCommunicationForCarrier(int subId) {
+        synchronized (mIsSatelliteEnabledLock) {
+            return !mSatelliteAttachRestrictionForCarrierArray
+                    .getOrDefault(subId, Collections.emptySet()).isEmpty();
+        }
+    }
+
+    /**
+     * Save user setting for enabling satellite attach for the carrier associated with the
+     * {@code subId} to persistent storage.
+     *
+     * @param subId Subscription ID.
+     *
+     * @return {@code true} if persist successful, {@code false} otherwise.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected boolean persistSatelliteAttachEnabledForCarrierSetting(int subId) {
+        logd("persistSatelliteAttachEnabledForCarrierSetting");
+        if (!isValidSubscriptionId(subId)) {
+            loge("persistSatelliteAttachEnabledForCarrierSetting: subId is not valid,"
+                    + " subId=" + subId);
+            return false;
+        }
+
+        synchronized (mIsSatelliteEnabledLock) {
+            try {
+                mSubscriptionManagerService.setSubscriptionProperty(subId,
+                        SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
+                        mSatelliteAttachRestrictionForCarrierArray.get(subId)
+                                .contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER)
+                                ? "0" : "1");
+            } catch (IllegalArgumentException | SecurityException ex) {
+                loge("persistSatelliteAttachEnabledForCarrierSetting, ex=" + ex);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Evaluate whether satellite attach for carrier should be restricted.
+     *
+     * @param subId Subscription Id to evaluate for.
+     * @return {@code true} satellite attach is restricted, {@code false} otherwise.
+     */
+    private boolean isSatelliteRestrictedForCarrier(int subId) {
+        return !isSatelliteAttachEnabledForCarrierByUser(subId)
+                || hasReasonToRestrictSatelliteCommunicationForCarrier(subId);
+    }
+
+    /**
+     * Check whether satellite is enabled for carrier at modem.
+     *
+     * @param subId Subscription ID to check for.
+     * @return {@code true} if satellite modem is enabled, {@code false} otherwise.
+     */
+    private boolean isSatelliteEnabledForCarrierAtModem(int subId) {
+        synchronized (mIsSatelliteEnabledLock) {
+            return mIsSatelliteAttachEnabledForCarrierArrayPerSub.getOrDefault(subId, false);
+        }
+    }
+
+    /**
+     * Evaluate whether satellite modem for carrier should be enabled or not.
+     * <p>
+     * Satellite will be enabled only when the following conditions are met:
+     * <ul>
+     * <li>Users want to enable it.</li>
+     * <li>There is no satellite communication restriction, which is added by
+     * {@link #addSatelliteAttachRestrictionForCarrier(int, int, IIntegerConsumer)}</li>
+     * <li>The carrier config {@link
+     * android.telephony.CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} is set to
+     * {@code true}.</li>
+     * </ul>
+     *
+     * @param subId Subscription Id for evaluate for.
+     * @param callback The callback for getting the result of enabling satellite.
+     */
+    private void evaluateEnablingSatelliteForCarrier(int subId, int reason,
+            @Nullable Consumer<Integer> callback) {
+        if (callback == null) {
+            callback = errorCode -> logd("evaluateEnablingSatelliteForCarrier: "
+                    + "SetSatelliteAttachEnableForCarrier error code =" + errorCode);
+        }
+
+        if (!isSatelliteSupportedForCarrier(subId)) {
+            logd("Satellite for carrier is not supported. Only user setting is stored");
+            callback.accept(SatelliteManager.SATELLITE_RESULT_SUCCESS);
+            return;
+        }
+
+        /* Request to enable or disable the satellite in the cellular modem only when the desired
+        state and the current state are different. */
+        boolean isSatelliteExpectedToBeEnabled = !isSatelliteRestrictedForCarrier(subId);
+        if (isSatelliteExpectedToBeEnabled != isSatelliteEnabledForCarrierAtModem(subId)) {
+            if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+                int simSlot = SubscriptionManager.getSlotIndex(subId);
+                RequestHandleSatelliteAttachRestrictionForCarrierArgument argument =
+                        new RequestHandleSatelliteAttachRestrictionForCarrierArgument(subId,
+                                reason, callback);
+                SatelliteControllerHandlerRequest request =
+                        new SatelliteControllerHandlerRequest(argument,
+                                SatelliteServiceUtils.getPhone(subId));
+                Message onCompleted = obtainMessage(
+                        EVENT_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE_DONE, request);
+                mSatelliteModemInterface.requestSetSatelliteEnabledForCarrier(simSlot,
+                        isSatelliteExpectedToBeEnabled, onCompleted);
+            } else {
+                callback.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
+            }
+        } else {
+            callback.accept(SatelliteManager.SATELLITE_RESULT_SUCCESS);
+        }
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index ebf7780..d03587f 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -49,6 +49,7 @@
 import com.android.internal.telephony.IIntegerConsumer;
 
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * Satellite modem interface to manage connections with the satellite service and HAL interface.
@@ -469,14 +470,14 @@
                 loge("requestSatelliteListeningEnabled: RemoteException " + e);
                 if (message != null) {
                     sendMessageWithResult(
-                            message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                            message, null, SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
                 }
             }
         } else {
             loge("requestSatelliteListeningEnabled: Satellite service is unavailable.");
             if (message != null) {
                 sendMessageWithResult(message, null,
-                        SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+                        SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
             }
         }
     }
@@ -508,14 +509,14 @@
                 loge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e);
                 if (message != null) {
                     sendMessageWithResult(
-                            message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                            message, null, SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
                 }
             }
         } else {
             loge("enableCellularModemWhileSatelliteModeIsOn: Satellite service is unavailable.");
             if (message != null) {
                 sendMessageWithResult(message, null,
-                        SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+                        SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
             }
         }
     }
@@ -544,11 +545,13 @@
                 });
             } catch (RemoteException e) {
                 loge("setSatelliteEnabled: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("setSatelliteEnabled: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -576,16 +579,18 @@
                         int[] enabled = new int[] {result ? 1 : 0};
                         logd("requestIsSatelliteEnabled: " + Arrays.toString(enabled));
                         Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
-                                message, enabled, SatelliteManager.SATELLITE_ERROR_NONE));
+                                message, enabled, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                     }
                 });
             } catch (RemoteException e) {
                 loge("requestIsSatelliteEnabled: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("requestIsSatelliteEnabled: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -610,17 +615,18 @@
                     public void accept(boolean result) {
                         logd("requestIsSatelliteSupported: " + result);
                         Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
-                                message, result, SatelliteManager.SATELLITE_ERROR_NONE));
+                                message, result, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                     }
                 });
             } catch (RemoteException e) {
                 loge("requestIsSatelliteSupported: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("requestIsSatelliteSupported: Satellite service is unavailable.");
             sendMessageWithResult(
-                    message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+                    message, null, SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -648,16 +654,18 @@
                                 SatelliteServiceUtils.fromSatelliteCapabilities(result);
                         logd("requestSatelliteCapabilities: " + capabilities);
                         Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
-                                message, capabilities, SatelliteManager.SATELLITE_ERROR_NONE));
+                                message, capabilities, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                     }
                 });
             } catch (RemoteException e) {
                 loge("requestSatelliteCapabilities: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("requestSatelliteCapabilities: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -682,11 +690,13 @@
                 });
             } catch (RemoteException e) {
                 loge("startSendingSatellitePointingInfo: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("startSendingSatellitePointingInfo: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -710,11 +720,13 @@
                 });
             } catch (RemoteException e) {
                 loge("stopSendingSatellitePointingInfo: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("stopSendingSatellitePointingInfo: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -744,11 +756,13 @@
                         });
             } catch (RemoteException e) {
                 loge("provisionSatelliteService: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("provisionSatelliteService: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -774,11 +788,13 @@
                 });
             } catch (RemoteException e) {
                 loge("deprovisionSatelliteService: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("deprovisionSatelliteService: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -806,16 +822,18 @@
                         int[] provisioned = new int[] {result ? 1 : 0};
                         logd("requestIsSatelliteProvisioned: " + Arrays.toString(provisioned));
                         Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
-                                message, provisioned, SatelliteManager.SATELLITE_ERROR_NONE));
+                                message, provisioned, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                     }
                 });
             } catch (RemoteException e) {
                 loge("requestIsSatelliteProvisioned: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("requestIsSatelliteProvisioned: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -840,11 +858,13 @@
                 });
             } catch (RemoteException e) {
                 loge("pollPendingSatelliteDatagrams: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("pollPendingSatelliteDatagrams: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -874,11 +894,13 @@
                         });
             } catch (RemoteException e) {
                 loge("sendSatelliteDatagram: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("sendSatelliteDatagram: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -907,16 +929,18 @@
                         int modemState = SatelliteServiceUtils.fromSatelliteModemState(result);
                         logd("requestSatelliteModemState: " + modemState);
                         Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
-                                message, modemState, SatelliteManager.SATELLITE_ERROR_NONE));
+                                message, modemState, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                     }
                 });
             } catch (RemoteException e) {
                 loge("requestSatelliteModemState: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("requestSatelliteModemState: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -944,18 +968,21 @@
                                 logd("requestIsSatelliteCommunicationAllowedForCurrentLocation: "
                                         + result);
                                 Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
-                                        message, result, SatelliteManager.SATELLITE_ERROR_NONE));
+                                        message, result,
+                                        SatelliteManager.SATELLITE_RESULT_SUCCESS));
                             }
                         });
             } catch (RemoteException e) {
                 loge("requestIsSatelliteCommunicationAllowedForCurrentLocation: RemoteException "
                         + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("requestIsSatelliteCommunicationAllowedForCurrentLocation: "
                     + "Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
@@ -987,19 +1014,142 @@
                                 logd("requestTimeForNextSatelliteVisibility: "
                                         + Arrays.toString(time));
                                 Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
-                                        message, time, SatelliteManager.SATELLITE_ERROR_NONE));
+                                        message, time, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                             }
                         });
             } catch (RemoteException e) {
                 loge("requestTimeForNextSatelliteVisibility: RemoteException " + e);
-                sendMessageWithResult(message, null, SatelliteManager.SATELLITE_SERVICE_ERROR);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
             loge("requestTimeForNextSatelliteVisibility: Satellite service is unavailable.");
-            sendMessageWithResult(message, null, SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE);
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
     }
 
+    /**
+     * Set the non-terrestrial PLMN with lower priority than terrestrial networks.
+     * MCC/MNC broadcast by the non-terrestrial networks will not be included in OPLMNwACT file
+     * on SIM profile.
+     * Acquisition of satellite based system is deemed lower priority to terrestrial networks.
+     * Even so, UE shall make all attempts to acquire terrestrial service prior to camping on
+     * satellite LTE service.
+     *
+     * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
+     *                this information to determine the relevant carrier.
+     * @param plmnList The list of roaming PLMN used for connecting to satellite networks.
+     * @param message The result receiver that returns whether the modem has
+     *               successfully set the satellite PLMN
+     */
+    public void setSatellitePlmn(@NonNull int simSlot, @NonNull List<String> plmnList,
+            @NonNull Message message) {
+        if (mSatelliteService != null) {
+            try {
+                mSatelliteService.setSatellitePlmn(simSlot, plmnList,
+                        new IIntegerConsumer.Stub() {
+                            @Override
+                            public void accept(int result) {
+                                int error = SatelliteServiceUtils.fromSatelliteError(result);
+                                logd("setSatellitePlmn: " + error);
+                                Binder.withCleanCallingIdentity(() ->
+                                        sendMessageWithResult(message, null, error));
+                            }
+                        });
+            } catch (RemoteException e) {
+                loge("setSatellitePlmn: RemoteException " + e);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
+            }
+        } else {
+            loge("setSatellitePlmn: Satellite service is unavailable.");
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
+        }
+    }
+
+    /**
+     * Enable or disable satellite in the cellular modem associated with a carrier.
+     * Refer setSatellitePlmn for the details of satellite PLMN scanning process.
+     *
+     * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
+     *                this information to determine the relevant carrier.
+     * @param enableSatellite True to enable the satellite modem and false to disable.
+     * @param message The Message to send to result of the operation to.
+     */
+    public void requestSetSatelliteEnabledForCarrier(@NonNull int simSlot,
+            @NonNull boolean enableSatellite, @NonNull Message message) {
+        if (mSatelliteService != null) {
+            try {
+                mSatelliteService.setSatelliteEnabledForCarrier(simSlot, enableSatellite,
+                        new IIntegerConsumer.Stub() {
+                            @Override
+                            public void accept(int result) {
+                                int error = SatelliteServiceUtils.fromSatelliteError(result);
+                                logd("requestSetSatelliteEnabledForCarrier: " + error);
+                                Binder.withCleanCallingIdentity(() ->
+                                        sendMessageWithResult(message, null, error));
+                            }
+                        });
+            } catch (RemoteException e) {
+                loge("requestSetSatelliteEnabledForCarrier: RemoteException " + e);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
+            }
+        } else {
+            loge("requestSetSatelliteEnabledForCarrier: Satellite service is unavailable.");
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+        }
+    }
+
+    /**
+     * Check whether satellite is enabled in the cellular modem associated with a carrier.
+     *
+     * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
+     *                this information to determine the relevant carrier.
+     * @param message The Message to send to result of the operation to.
+     */
+    public void requestIsSatelliteEnabledForCarrier(@NonNull int simSlot,
+            @NonNull Message message) {
+        if (mSatelliteService != null) {
+            try {
+                mSatelliteService.requestIsSatelliteEnabledForCarrier(simSlot,
+                        new IIntegerConsumer.Stub() {
+                            @Override
+                            public void accept(int result) {
+                                int error = SatelliteServiceUtils.fromSatelliteError(result);
+                                logd("requestIsSatelliteEnabledForCarrier: " + error);
+                                Binder.withCleanCallingIdentity(() ->
+                                        sendMessageWithResult(message, null, error));
+                            }
+                        }, new IBooleanConsumer.Stub() {
+                            @Override
+                            public void accept(boolean result) {
+                                // Convert for compatibility with SatelliteResponse
+                                // TODO: This should just report result instead.
+                                int[] enabled = new int[] {result ? 1 : 0};
+                                logd("requestIsSatelliteEnabledForCarrier: "
+                                        + Arrays.toString(enabled));
+                                Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
+                                        message, enabled,
+                                        SatelliteManager.SATELLITE_RESULT_SUCCESS));
+                            }
+                        });
+            } catch (RemoteException e) {
+                loge("requestIsSatelliteEnabledForCarrier: RemoteException " + e);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
+            }
+        } else {
+            loge("requestIsSatelliteEnabledForCarrier: Satellite service is unavailable.");
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
+        }
+    }
+
+
     public boolean isSatelliteServiceSupported() {
         return mIsSatelliteServiceSupported;
     }
@@ -1036,8 +1186,8 @@
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected static void sendMessageWithResult(@NonNull Message message, @Nullable Object result,
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = error == SatelliteManager.SATELLITE_ERROR_NONE
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = error == SatelliteManager.SATELLITE_RESULT_SUCCESS
                 ? null : new SatelliteException(error);
         AsyncResult.forMessage(message, result, exception);
         message.sendToTarget();
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
index 25657b3..b098625 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -17,7 +17,7 @@
 package com.android.internal.telephony.satellite;
 
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_ERROR_NONE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import android.annotation.NonNull;
 import android.os.AsyncResult;
@@ -27,11 +27,11 @@
 import android.os.Message;
 import android.os.ResultReceiver;
 import android.provider.DeviceConfig;
-import android.telecom.Call;
 import android.telecom.Connection;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.RegistrationManager;
@@ -148,7 +148,7 @@
         mReceiverForRequestIsSatelliteAllowedForCurrentLocation = new ResultReceiver(this) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
-                if (resultCode == SATELLITE_ERROR_NONE) {
+                if (resultCode == SATELLITE_RESULT_SUCCESS) {
                     if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
                         boolean isSatelliteCommunicationAllowed =
                                 resultData.getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED);
@@ -259,7 +259,8 @@
                 && mSatelliteController.isSatelliteProvisioned()
                 && shouldTrackCall(mEmergencyConnection.getState())) {
             logd("handleTimeoutEvent: Sending EVENT_DISPLAY_SOS_MESSAGE to Dialer...");
-            mEmergencyConnection.sendConnectionEvent(Call.EVENT_DISPLAY_SOS_MESSAGE, null);
+            mEmergencyConnection.sendConnectionEvent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE,
+                    null);
             isDialerNotified = true;
         }
         reportEsosRecommenderDecision(isDialerNotified);
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
index 151b69d..ba68f8d 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
@@ -19,6 +19,8 @@
 import static android.telephony.NetworkRegistrationInfo.FIRST_SERVICE_TYPE;
 import static android.telephony.NetworkRegistrationInfo.LAST_SERVICE_TYPE;
 
+import static java.util.stream.Collectors.joining;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -34,8 +36,8 @@
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.stub.NTRadioTechnology;
-import android.telephony.satellite.stub.SatelliteError;
 import android.telephony.satellite.stub.SatelliteModemState;
+import android.telephony.satellite.stub.SatelliteResult;
 
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.Phone;
@@ -81,53 +83,51 @@
     /**
      * Convert satellite error from service definition to framework definition.
      * @param error The SatelliteError from the satellite service.
-     * @return The converted SatelliteError for the framework.
+     * @return The converted SatelliteResult for the framework.
      */
-    @SatelliteManager.SatelliteError public static int fromSatelliteError(int error) {
+    @SatelliteManager.SatelliteResult public static int fromSatelliteError(int error) {
         switch (error) {
-            case SatelliteError.ERROR_NONE:
-                return SatelliteManager.SATELLITE_ERROR_NONE;
-            case SatelliteError.SATELLITE_ERROR:
-                return SatelliteManager.SATELLITE_ERROR;
-            case SatelliteError.SERVER_ERROR:
-                return SatelliteManager.SATELLITE_SERVER_ERROR;
-            case SatelliteError.SERVICE_ERROR:
-                return SatelliteManager.SATELLITE_SERVICE_ERROR;
-            case SatelliteError.MODEM_ERROR:
-                return SatelliteManager.SATELLITE_MODEM_ERROR;
-            case SatelliteError.NETWORK_ERROR:
-                return SatelliteManager.SATELLITE_NETWORK_ERROR;
-            case SatelliteError.INVALID_TELEPHONY_STATE:
-                return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
-            case SatelliteError.INVALID_MODEM_STATE:
-                return SatelliteManager.SATELLITE_INVALID_MODEM_STATE;
-            case SatelliteError.INVALID_ARGUMENTS:
-                return SatelliteManager.SATELLITE_INVALID_ARGUMENTS;
-            case SatelliteError.REQUEST_FAILED:
-                return SatelliteManager.SATELLITE_REQUEST_FAILED;
-            case SatelliteError.RADIO_NOT_AVAILABLE:
-                return SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE;
-            case SatelliteError.REQUEST_NOT_SUPPORTED:
-                return SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED;
-            case SatelliteError.NO_RESOURCES:
-                return SatelliteManager.SATELLITE_NO_RESOURCES;
-            case SatelliteError.SERVICE_NOT_PROVISIONED:
-                return SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED;
-            case SatelliteError.SERVICE_PROVISION_IN_PROGRESS:
-                return SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS;
-            case SatelliteError.REQUEST_ABORTED:
-                return SatelliteManager.SATELLITE_REQUEST_ABORTED;
-            case SatelliteError.SATELLITE_ACCESS_BARRED:
-                return SatelliteManager.SATELLITE_ACCESS_BARRED;
-            case SatelliteError.NETWORK_TIMEOUT:
-                return SatelliteManager.SATELLITE_NETWORK_TIMEOUT;
-            case SatelliteError.SATELLITE_NOT_REACHABLE:
-                return SatelliteManager.SATELLITE_NOT_REACHABLE;
-            case SatelliteError.NOT_AUTHORIZED:
-                return SatelliteManager.SATELLITE_NOT_AUTHORIZED;
+            case SatelliteResult.SATELLITE_RESULT_SUCCESS:
+                return SatelliteManager.SATELLITE_RESULT_SUCCESS;
+            case SatelliteResult.SATELLITE_RESULT_ERROR:
+                return SatelliteManager.SATELLITE_RESULT_ERROR;
+            case SatelliteResult.SATELLITE_RESULT_SERVER_ERROR:
+                return SatelliteManager.SATELLITE_RESULT_SERVER_ERROR;
+            case SatelliteResult.SATELLITE_RESULT_SERVICE_ERROR:
+                return SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR;
+            case SatelliteResult.SATELLITE_RESULT_MODEM_ERROR:
+                return SatelliteManager.SATELLITE_RESULT_MODEM_ERROR;
+            case SatelliteResult.SATELLITE_RESULT_NETWORK_ERROR:
+                return SatelliteManager.SATELLITE_RESULT_NETWORK_ERROR;
+            case SatelliteResult.SATELLITE_RESULT_INVALID_MODEM_STATE:
+                return SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE;
+            case SatelliteResult.SATELLITE_RESULT_INVALID_ARGUMENTS:
+                return SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS;
+            case SatelliteResult.SATELLITE_RESULT_REQUEST_FAILED:
+                return SatelliteManager.SATELLITE_RESULT_REQUEST_FAILED;
+            case SatelliteResult.SATELLITE_RESULT_RADIO_NOT_AVAILABLE:
+                return SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE;
+            case SatelliteResult.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED:
+                return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
+            case SatelliteResult.SATELLITE_RESULT_NO_RESOURCES:
+                return SatelliteManager.SATELLITE_RESULT_NO_RESOURCES;
+            case SatelliteResult.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED:
+                return SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED;
+            case SatelliteResult.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS:
+                return SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS;
+            case SatelliteResult.SATELLITE_RESULT_REQUEST_ABORTED:
+                return SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED;
+            case SatelliteResult.SATELLITE_RESULT_ACCESS_BARRED:
+                return SatelliteManager.SATELLITE_RESULT_ACCESS_BARRED;
+            case SatelliteResult.SATELLITE_RESULT_NETWORK_TIMEOUT:
+                return SatelliteManager.SATELLITE_RESULT_NETWORK_TIMEOUT;
+            case SatelliteResult.SATELLITE_RESULT_NOT_REACHABLE:
+                return SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE;
+            case SatelliteResult.SATELLITE_RESULT_NOT_AUTHORIZED:
+                return SatelliteManager.SATELLITE_RESULT_NOT_AUTHORIZED;
         }
         loge("Received invalid satellite service error: " + error);
-        return SatelliteManager.SATELLITE_SERVICE_ERROR;
+        return SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR;
     }
 
     /**
@@ -150,6 +150,10 @@
                 return SatelliteManager.SATELLITE_MODEM_STATE_OFF;
             case SatelliteModemState.SATELLITE_MODEM_STATE_UNAVAILABLE:
                 return SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE;
+            case SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED:
+                return SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED;
+            case SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED:
+                return SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
             default:
                 loge("Received invalid modem state: " + modemState);
                 return SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
@@ -222,20 +226,20 @@
     }
 
     /**
-     * Get the {@link SatelliteManager.SatelliteError} from the provided result.
+     * Get the {@link SatelliteManager.SatelliteResult} from the provided result.
      *
      * @param ar AsyncResult used to determine the error code.
      * @param caller The satellite request.
      *
-     * @return The {@link SatelliteManager.SatelliteError} error code from the request.
+     * @return The {@link SatelliteManager.SatelliteResult} error code from the request.
      */
-    @SatelliteManager.SatelliteError public static int getSatelliteError(@NonNull AsyncResult ar,
+    @SatelliteManager.SatelliteResult public static int getSatelliteError(@NonNull AsyncResult ar,
             @NonNull String caller) {
         int errorCode;
         if (ar.exception == null) {
-            errorCode = SatelliteManager.SATELLITE_ERROR_NONE;
+            errorCode = SatelliteManager.SATELLITE_RESULT_SUCCESS;
         } else {
-            errorCode = SatelliteManager.SATELLITE_ERROR;
+            errorCode = SatelliteManager.SATELLITE_RESULT_ERROR;
             if (ar.exception instanceof CommandException) {
                 CommandException.Error error = ((CommandException) ar.exception).getCommandError();
                 errorCode = RILUtils.convertToSatelliteError(error);
@@ -316,6 +320,9 @@
                         }
                     }
                 }
+                logd("parseSupportedSatelliteServices: plmn=" + plmn + ", supportedServicesSet="
+                        + supportedServicesSet.stream().map(String::valueOf).collect(
+                                joining(",")));
                 supportedServicesMap.put(plmn, supportedServicesSet);
             } else {
                 loge("parseSupportedSatelliteServices: invalid format input, "
@@ -353,17 +360,18 @@
                             + " for plmn=" + plmn);
                 }
             }
+            logd("parseSupportedSatelliteServices: plmn=" + plmn + ", supportedServicesSet="
+                    + supportedServicesSet.stream().map(String::valueOf).collect(
+                            joining(",")));
             supportedServicesMap.put(plmn, supportedServicesSet);
         }
         return supportedServicesMap;
     }
 
     /**
-     * For the PLMN that exists in both {@code providerSupportedServices} and
-     * {@code carrierSupportedServices}, the supported services will be the intersection of the two
-     * sets. For the PLMN that is present in {@code providerSupportedServices} but not in
-     * {@code carrierSupportedServices}, the provider supported services will be used. The rest
-     * will not be used.
+     * For the PLMN that exists in {@code carrierSupportedServices}, the carrier supported services
+     * will be used. For the PLMN that is present in {@code providerSupportedServices} but not in
+     * {@code carrierSupportedServices}, the provider supported services will be used.
      *
      * @param providerSupportedServices Satellite provider supported satellite services.
      * @param carrierSupportedServices Carrier supported satellite services.
@@ -379,12 +387,15 @@
                     carrierSupportedServices) {
         Map<String, Set<Integer>> supportedServicesMap = new HashMap<>();
         for (Map.Entry<String, Set<Integer>> entry : providerSupportedServices.entrySet()) {
-            Set<Integer> supportedServices = new HashSet<>(entry.getValue());
-            if (carrierSupportedServices.containsKey(entry.getKey())) {
-                supportedServices.retainAll(carrierSupportedServices.get(entry.getKey()));
+            if (!entry.getValue().isEmpty()) {
+                supportedServicesMap.put(entry.getKey(), entry.getValue());
             }
-            if (!supportedServices.isEmpty()) {
-                supportedServicesMap.put(entry.getKey(), supportedServices);
+        }
+        for (Map.Entry<String, Set<Integer>> entry : carrierSupportedServices.entrySet()) {
+            if (entry.getValue().isEmpty()) {
+                supportedServicesMap.remove(entry.getKey());
+            } else {
+                supportedServicesMap.put(entry.getKey(), entry.getValue());
             }
         }
         return supportedServicesMap;
@@ -403,6 +414,15 @@
         return PhoneFactory.getPhone(0);
     }
 
+    /**
+     * Return phone associated with subscription ID.
+     *
+     * @return phone associated with {@code subId} or {@code null} if it doesn't exist.
+     */
+    public static @Nullable Phone getPhone(int subId) {
+        return PhoneFactory.getPhone(subId);
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index 36ad250..d84ceeb 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -30,13 +30,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.AsyncResult;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.provider.DeviceConfig;
 import android.telephony.Rlog;
 import android.telephony.satellite.ISatelliteStateCallback;
 import android.telephony.satellite.SatelliteManager;
@@ -46,6 +46,7 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ExponentialBackoff;
 import com.android.internal.util.State;
@@ -95,6 +96,11 @@
     private static final int EVENT_DATAGRAM_TRANSFER_STATE_CHANGED = 1;
     private static final int EVENT_LISTENING_TIMER_TIMEOUT = 2;
     private static final int EVENT_SATELLITE_ENABLED_STATE_CHANGED = 3;
+    private static final int EVENT_SATELLITE_MODEM_STATE_CHANGED = 4;
+    private static final int EVENT_SATELLITE_DATAGRAMS_TRANSFER_REQUESTED = 5;
+    private static final int EVENT_DISABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE = 6;
+    protected static final int EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT = 7;
+    private static final int EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMER_TIMED_OUT = 8;
 
     private static final long REBIND_INITIAL_DELAY = 2 * 1000; // 2 seconds
     private static final long REBIND_MAXIMUM_DELAY = 64 * 1000; // 1 minute
@@ -117,14 +123,20 @@
     @NonNull private final IdleState mIdleState = new IdleState();
     @NonNull private final TransferringState mTransferringState = new TransferringState();
     @NonNull private final ListeningState mListeningState = new ListeningState();
+    @NonNull private final NotConnectedState mNotConnectedState = new NotConnectedState();
+    @NonNull private final ConnectedState mConnectedState = new ConnectedState();
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected AtomicBoolean mIsSendingTriggeredDuringTransferringState;
     private long mSatelliteStayAtListeningFromSendingMillis;
     private long mSatelliteStayAtListeningFromReceivingMillis;
+    private long mSatelliteNbIotInactivityTimeoutMillis;
     private final ConcurrentHashMap<IBinder, ISatelliteStateCallback> mListeners;
     @SatelliteManager.SatelliteModemState private int mCurrentState;
     final boolean mIsSatelliteSupported;
     private boolean mIsDemoMode = false;
+    @GuardedBy("mLock")
+    @NonNull private boolean mIsDisableCellularModemInProgress = false;
+    @NonNull private final SatelliteController mSatelliteController;
 
     /**
      * @return The singleton instance of SatelliteSessionController.
@@ -148,9 +160,7 @@
             @NonNull Context context, @NonNull Looper looper, boolean isSatelliteSupported) {
         if (sInstance == null) {
             sInstance = new SatelliteSessionController(context, looper, isSatelliteSupported,
-                    SatelliteModemInterface.getInstance(),
-                    getSatelliteStayAtListeningFromSendingMillis(),
-                    getSatelliteStayAtListeningFromReceivingMillis());
+                    SatelliteModemInterface.getInstance());
         } else {
             if (isSatelliteSupported != sInstance.mIsSatelliteSupported) {
                 Rlog.e(TAG, "New satellite support state " + isSatelliteSupported
@@ -168,23 +178,21 @@
      * @param looper The looper associated with the handler of this class.
      * @param isSatelliteSupported Whether satellite is supported on the device.
      * @param satelliteModemInterface The singleton of SatelliteModemInterface.
-     * @param satelliteStayAtListeningFromSendingMillis The duration to stay at listening mode when
-     *                                                    transitioning from sending mode.
-     * @param satelliteStayAtListeningFromReceivingMillis The duration to stay at listening mode
-     *                                                    when transitioning from receiving mode.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected SatelliteSessionController(@NonNull Context context, @NonNull Looper looper,
             boolean isSatelliteSupported,
-            @NonNull SatelliteModemInterface satelliteModemInterface,
-            long satelliteStayAtListeningFromSendingMillis,
-            long satelliteStayAtListeningFromReceivingMillis) {
+            @NonNull SatelliteModemInterface satelliteModemInterface) {
         super(TAG, looper);
 
         mContext = context;
         mSatelliteModemInterface = satelliteModemInterface;
-        mSatelliteStayAtListeningFromSendingMillis = satelliteStayAtListeningFromSendingMillis;
-        mSatelliteStayAtListeningFromReceivingMillis = satelliteStayAtListeningFromReceivingMillis;
+        mSatelliteController = SatelliteController.getInstance();
+        mSatelliteStayAtListeningFromSendingMillis = getSatelliteStayAtListeningFromSendingMillis();
+        mSatelliteStayAtListeningFromReceivingMillis =
+                getSatelliteStayAtListeningFromReceivingMillis();
+        mSatelliteNbIotInactivityTimeoutMillis =
+                getSatelliteNbIotInactivityTimeoutMillis();
         mListeners = new ConcurrentHashMap<>();
         mIsSendingTriggeredDuringTransferringState = new AtomicBoolean(false);
         mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
@@ -211,6 +219,8 @@
         addState(mIdleState);
         addState(mTransferringState);
         addState(mListeningState, mTransferringState);
+        addState(mNotConnectedState);
+        addState(mConnectedState);
         setInitialState(isSatelliteSupported);
         start();
     }
@@ -245,6 +255,37 @@
     }
 
     /**
+     * {@link DatagramDispatcher} and {@link DatagramReceiver} use this function to notify
+     * {@link SatelliteSessionController} that it has received a request to send satellite
+     * datagrams or poll pending satellite datagrams.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void onSatelliteDatagramsTransferRequested() {
+        sendMessage(EVENT_SATELLITE_DATAGRAMS_TRANSFER_REQUESTED);
+    }
+
+    /**
+     * {@link DatagramDispatcher} and {@link DatagramReceiver} use this function to notify
+     * {@link SatelliteSessionController} that the datagram wait for connected state timer has
+     * timed out.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void onDatagramWaitForConnectedStateTimerTimedOut() {
+        sendMessage(EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMER_TIMED_OUT);
+    }
+
+    /**
+     * {@link SatelliteController} uses this function to notify {@link SatelliteSessionController}
+     * that the satellite modem state has changed.
+     *
+     * @param state The current state of the satellite modem.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) {
+        sendMessage(EVENT_SATELLITE_MODEM_STATE_CHANGED, state);
+    }
+
+    /**
      * Registers for modem state changed from satellite modem.
      *
      * @param callback The callback to handle the satellite modem state changed event.
@@ -289,9 +330,12 @@
                     getSatelliteStayAtListeningFromSendingMillis();
             mSatelliteStayAtListeningFromReceivingMillis =
                     getSatelliteStayAtListeningFromReceivingMillis();
+            mSatelliteNbIotInactivityTimeoutMillis =
+                    getSatelliteNbIotInactivityTimeoutMillis();
         } else {
             mSatelliteStayAtListeningFromSendingMillis = timeoutMillis;
             mSatelliteStayAtListeningFromReceivingMillis = timeoutMillis;
+            mSatelliteNbIotInactivityTimeoutMillis = timeoutMillis;
         }
 
         return true;
@@ -379,7 +423,11 @@
 
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_OFF;
             mIsSendingTriggeredDuringTransferringState.set(false);
+            synchronized (mLock) {
+                mIsDisableCellularModemInProgress = false;
+            }
             unbindService();
+            stopNbIotInactivityTimer();
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_OFF);
         }
 
@@ -404,7 +452,11 @@
 
         private void handleSatelliteEnabledStateChanged(boolean on) {
             if (on) {
-                transitionTo(mIdleState);
+                if (mSatelliteController.isSatelliteAttachRequired()) {
+                    transitionTo(mNotConnectedState);
+                } else {
+                    transitionTo(mIdleState);
+                }
             }
         }
     }
@@ -415,6 +467,7 @@
             if (DBG) logd("Entering IdleState");
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_IDLE;
             mIsSendingTriggeredDuringTransferringState.set(false);
+            stopNbIotInactivityTimer();
             //Enable Cellular Modem scanning
             mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(true, null);
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
@@ -430,6 +483,13 @@
                 case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
                     handleSatelliteEnabledStateChanged(!(boolean) msg.obj, "IdleState");
                     break;
+                case EVENT_SATELLITE_DATAGRAMS_TRANSFER_REQUESTED:
+                    disableCellularModemWhileSatelliteModeIsOn();
+                    break;
+                case EVENT_DISABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE:
+                    handleEventDisableCellularModemWhileSatelliteModeIsOnDone(
+                            (AsyncResult) msg.obj);
+                    break;
             }
             // Ignore all unexpected events.
             return HANDLED;
@@ -440,15 +500,52 @@
             if ((datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING)
                     || (datagramTransferState.receiveState
                     == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING)) {
-                transitionTo(mTransferringState);
+                if (mSatelliteController.isSatelliteAttachRequired()) {
+                    loge("Unexpected transferring state received for NB-IOT NTN");
+                } else {
+                    transitionTo(mTransferringState);
+                }
+            }
+        }
+
+        private void handleEventDisableCellularModemWhileSatelliteModeIsOnDone(
+                @NonNull AsyncResult result) {
+            synchronized (mLock) {
+                if (mIsDisableCellularModemInProgress) {
+                    int error = SatelliteServiceUtils.getSatelliteError(
+                            result, "DisableCellularModemWhileSatelliteModeIsOnDone");
+                    if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
+                        transitionTo(mNotConnectedState);
+                    }
+                    mIsDisableCellularModemInProgress = false;
+                } else {
+                    loge("DisableCellularModemWhileSatelliteModeIsOn is not in progress");
+                }
+            }
+        }
+
+        private void disableCellularModemWhileSatelliteModeIsOn() {
+            synchronized (mLock) {
+                if (mIsDisableCellularModemInProgress) {
+                    logd("Cellular scanning is already being disabled");
+                    return;
+                }
+
+                mIsDisableCellularModemInProgress = true;
+                Message onCompleted =
+                        obtainMessage(EVENT_DISABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE);
+                mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false,
+                        onCompleted);
             }
         }
 
         @Override
         public void exit() {
             if (DBG) logd("Exiting IdleState");
-            //Disable Cellular Modem Scanning
-            mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false, null);
+            if (!mSatelliteController.isSatelliteAttachRequired()) {
+                // Disable cellular modem scanning
+                mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false, null);
+            }
         }
     }
 
@@ -456,6 +553,7 @@
         @Override
         public void enter() {
             if (DBG) logd("Entering TransferringState");
+            stopNbIotInactivityTimer();
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
         }
@@ -470,6 +568,9 @@
                 case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
                     handleSatelliteEnabledStateChanged(!(boolean) msg.obj, "TransferringState");
                     break;
+                case EVENT_SATELLITE_MODEM_STATE_CHANGED:
+                    handleEventSatelliteModemStateChange(msg.arg1);
+                    break;
             }
             // Ignore all unexpected events.
             return HANDLED;
@@ -480,13 +581,26 @@
             if (isSending(datagramTransferState.sendState) || isReceiving(
                     datagramTransferState.receiveState)) {
                 // Stay at transferring state.
-            } else if ((datagramTransferState.sendState
-                    == SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED)
-                    || (datagramTransferState.receiveState
-                    == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED)) {
-                transitionTo(mIdleState);
             } else {
-                transitionTo(mListeningState);
+                if (mSatelliteController.isSatelliteAttachRequired()) {
+                    transitionTo(mConnectedState);
+                } else {
+                    if ((datagramTransferState.sendState
+                            == SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED)
+                            || (datagramTransferState.receiveState
+                            == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED)) {
+                        transitionTo(mIdleState);
+                    } else {
+                        transitionTo(mListeningState);
+                    }
+                }
+            }
+        }
+
+        private void handleEventSatelliteModemStateChange(
+                @SatelliteManager.SatelliteModemState int state) {
+            if (state == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED) {
+                transitionTo(mNotConnectedState);
             }
         }
     }
@@ -505,6 +619,8 @@
 
         @Override
         public void exit() {
+            if (DBG) logd("Exiting ListeningState");
+
             removeMessages(EVENT_LISTENING_TIMER_TIMEOUT);
             updateListeningMode(false);
         }
@@ -549,6 +665,108 @@
         }
     }
 
+    private class NotConnectedState extends State {
+        @Override
+        public void enter() {
+            if (DBG) logd("Entering NotConnectedState");
+
+            mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED;
+            notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+            startNbIotInactivityTimer();
+        }
+
+        @Override
+        public void exit() {
+            if (DBG) logd("Exiting NotConnectedState");
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (DBG) log("NotConnectedState: processing " + getWhatToString(msg.what));
+            switch (msg.what) {
+                case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
+                    handleSatelliteEnabledStateChanged(
+                            !(boolean) msg.obj, "NotConnectedState");
+                    break;
+                case EVENT_SATELLITE_MODEM_STATE_CHANGED:
+                    handleEventSatelliteModemStateChanged(msg.arg1);
+                    break;
+                case EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT:
+                    transitionTo(mIdleState);
+                    break;
+                case EVENT_SATELLITE_DATAGRAMS_TRANSFER_REQUESTED:
+                    stopNbIotInactivityTimer();
+                    break;
+                case EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMER_TIMED_OUT:
+                    startNbIotInactivityTimer();
+                    break;
+            }
+            // Ignore all unexpected events.
+            return HANDLED;
+        }
+
+        private void handleEventSatelliteModemStateChanged(
+                @SatelliteManager.SatelliteModemState int state) {
+            if (state == SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED) {
+                transitionTo(mConnectedState);
+            }
+        }
+    }
+
+    private class ConnectedState extends State {
+        @Override
+        public void enter() {
+            if (DBG) logd("Entering ConnectedState");
+
+            mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
+            notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+            startNbIotInactivityTimer();
+        }
+
+        @Override
+        public void exit() {
+            if (DBG) logd("Exiting ConnectedState");
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (DBG) log("ConnectedState: processing " + getWhatToString(msg.what));
+            switch (msg.what) {
+                case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
+                    handleSatelliteEnabledStateChanged(
+                            !(boolean) msg.obj, "ConnectedState");
+                    break;
+                case EVENT_SATELLITE_MODEM_STATE_CHANGED:
+                    handleEventSatelliteModemStateChanged(msg.arg1);
+                    break;
+                case EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT:
+                    transitionTo(mIdleState);
+                    break;
+                case EVENT_DATAGRAM_TRANSFER_STATE_CHANGED:
+                    handleEventDatagramTransferStateChanged((DatagramTransferState) msg.obj);
+                    break;
+            }
+            // Ignore all unexpected events.
+            return HANDLED;
+        }
+
+        private void handleEventSatelliteModemStateChanged(
+                @SatelliteManager.SatelliteModemState int state) {
+            if (state == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED) {
+                transitionTo(mNotConnectedState);
+            }
+        }
+
+        private void handleEventDatagramTransferStateChanged(
+                @NonNull DatagramTransferState datagramTransferState) {
+            if (datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING
+                    || datagramTransferState.receiveState
+                    == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING) {
+                transitionTo(mTransferringState);
+            }
+        }
+    }
+
     /**
      * @return the string for msg.what
      */
@@ -565,6 +783,21 @@
             case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
                 whatString = "EVENT_SATELLITE_ENABLED_STATE_CHANGED";
                 break;
+            case EVENT_DISABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE:
+                whatString = "EVENT_DISABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE";
+                break;
+            case EVENT_SATELLITE_DATAGRAMS_TRANSFER_REQUESTED:
+                whatString = "EVENT_SATELLITE_DATAGRAMS_TRANSFER_REQUESTED";
+                break;
+            case EVENT_SATELLITE_MODEM_STATE_CHANGED:
+                whatString = "EVENT_SATELLITE_MODEM_STATE_CHANGED";
+                break;
+            case EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT:
+                whatString = "EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT";
+                break;
+            case EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMER_TIMED_OUT:
+                whatString = "EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMER_TIMED_OUT";
+                break;
             default:
                 whatString = "UNKNOWN EVENT " + what;
         }
@@ -720,23 +953,49 @@
         return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
     }
 
-    private static long getSatelliteStayAtListeningFromSendingMillis() {
-        if (sInstance != null && sInstance.isDemoMode()) {
+    private long getSatelliteStayAtListeningFromSendingMillis() {
+        if (isDemoMode()) {
             return DEMO_MODE_SATELLITE_STAY_AT_LISTENING_MILLIS;
         } else {
-            return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
-                    SATELLITE_STAY_AT_LISTENING_FROM_SENDING_MILLIS,
-                    DEFAULT_SATELLITE_STAY_AT_LISTENING_FROM_SENDING_MILLIS);
+            return mContext.getResources().getInteger(
+                    R.integer.config_satellite_stay_at_listening_from_sending_millis);
         }
     }
 
-    private static long getSatelliteStayAtListeningFromReceivingMillis() {
-        if (sInstance != null && sInstance.isDemoMode()) {
+    private long getSatelliteStayAtListeningFromReceivingMillis() {
+        if (isDemoMode()) {
             return DEMO_MODE_SATELLITE_STAY_AT_LISTENING_MILLIS;
         } else {
-            return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
-                    SATELLITE_STAY_AT_LISTENING_FROM_RECEIVING_MILLIS,
-                    DEFAULT_SATELLITE_STAY_AT_LISTENING_FROM_RECEIVING_MILLIS);
+            return mContext.getResources().getInteger(
+                    R.integer.config_satellite_stay_at_listening_from_receiving_millis);
         }
     }
+
+    private long getSatelliteNbIotInactivityTimeoutMillis() {
+        return mContext.getResources().getInteger(
+                R.integer.config_satellite_nb_iot_inactivity_timeout_millis);
+    }
+
+    private void startNbIotInactivityTimer() {
+        if (isNbIotInactivityTimerStarted()) {
+            logd("NB IOT inactivity timer is already started");
+            return;
+        }
+
+        DatagramController datagramController = DatagramController.getInstance();
+        if (datagramController.isSendingInIdleState()
+                && datagramController.isPollingInIdleState()) {
+            sendMessageDelayed(
+                    EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT,
+                    mSatelliteNbIotInactivityTimeoutMillis);
+        }
+    }
+
+    private void stopNbIotInactivityTimer() {
+        removeMessages(EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT);
+    }
+
+    private boolean isNbIotInactivityTimerStarted() {
+        return hasMessages(EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
index 7a1de7c..0c18fac 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
@@ -178,9 +178,9 @@
 
     /** Report a counter when an attempt for incoming datagram is failed */
     public void reportIncomingDatagramCount(
-            @NonNull @SatelliteManager.SatelliteError int result) {
+            @NonNull @SatelliteManager.SatelliteResult int result) {
         SatelliteStats.SatelliteControllerParams controllerParam;
-        if (result == SatelliteManager.SATELLITE_ERROR_NONE) {
+        if (result == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
             controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
                     .setCountOfIncomingDatagramSuccess(ADD_COUNT)
                     .build();
@@ -194,9 +194,9 @@
     }
 
     /** Report a counter when an attempt for de-provision is success or not */
-    public void reportProvisionCount(@NonNull @SatelliteManager.SatelliteError int result) {
+    public void reportProvisionCount(@NonNull @SatelliteManager.SatelliteResult int result) {
         SatelliteStats.SatelliteControllerParams controllerParam;
-        if (result == SatelliteManager.SATELLITE_ERROR_NONE) {
+        if (result == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
             controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
                     .setCountOfProvisionSuccess(ADD_COUNT)
                     .build();
@@ -210,9 +210,9 @@
     }
 
     /** Report a counter when an attempt for de-provision is success or not */
-    public void reportDeprovisionCount(@NonNull @SatelliteManager.SatelliteError int result) {
+    public void reportDeprovisionCount(@NonNull @SatelliteManager.SatelliteResult int result) {
         SatelliteStats.SatelliteControllerParams controllerParam;
-        if (result == SatelliteManager.SATELLITE_ERROR_NONE) {
+        if (result == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
             controllerParam = new SatelliteStats.SatelliteControllerParams.Builder()
                     .setCountOfDeprovisionSuccess(ADD_COUNT)
                     .build();
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
index 38696aa..d48c488 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
@@ -58,7 +58,7 @@
     }
 
     /** Sets the resultCode for provision metrics */
-    public ProvisionMetricsStats setResultCode(@SatelliteManager.SatelliteError int error) {
+    public ProvisionMetricsStats setResultCode(@SatelliteManager.SatelliteResult int error) {
         mResultCode = error;
         return this;
     }
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
index 776ba64..6585bec 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -30,7 +30,7 @@
     private static final boolean DBG = false;
 
     private static SessionMetricsStats sInstance = null;
-    private @SatelliteManager.SatelliteError int mInitializationResult;
+    private @SatelliteManager.SatelliteResult int mInitializationResult;
     private @SatelliteManager.NTRadioTechnology int mRadioTechnology;
 
     private SessionMetricsStats() {
@@ -54,7 +54,7 @@
 
     /** Sets the satellite initialization result */
     public SessionMetricsStats setInitializationResult(
-            @SatelliteManager.SatelliteError int result) {
+            @SatelliteManager.SatelliteResult int result) {
         logd("setInitializationResult(" + result + ")");
         mInitializationResult = result;
         return this;
@@ -81,7 +81,7 @@
     }
 
     private void initializeSessionMetricsParam() {
-        mInitializationResult = SatelliteManager.SATELLITE_ERROR_NONE;
+        mInitializationResult = SatelliteManager.SATELLITE_RESULT_SUCCESS;
         mRadioTechnology = SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN;
     }
 
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
index 124437c..3fcbdb8 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
@@ -274,7 +274,11 @@
                     SubscriptionInfoInternal::getUserId),
             new AbstractMap.SimpleImmutableEntry<>(
                     SimInfo.COLUMN_SATELLITE_ENABLED,
-                    SubscriptionInfoInternal::getSatelliteEnabled)
+                    SubscriptionInfoInternal::getSatelliteEnabled),
+            new AbstractMap.SimpleImmutableEntry<>(
+                    SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
+                    SubscriptionInfoInternal::getSatelliteAttachEnabledForCarrier)
+
     );
 
     /**
@@ -399,7 +403,10 @@
                     SubscriptionDatabaseManager::setUserId),
             new AbstractMap.SimpleImmutableEntry<>(
                     SimInfo.COLUMN_SATELLITE_ENABLED,
-                    SubscriptionDatabaseManager::setSatelliteEnabled)
+                    SubscriptionDatabaseManager::setSatelliteEnabled),
+            new AbstractMap.SimpleImmutableEntry<>(
+                    SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
+                    SubscriptionDatabaseManager::setSatelliteAttachEnabledForCarrier)
     );
 
     /**
@@ -508,7 +515,9 @@
             SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
             SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
             SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
-            SimInfo.COLUMN_USER_HANDLE
+            SimInfo.COLUMN_USER_HANDLE,
+            SimInfo.COLUMN_SATELLITE_ENABLED,
+            SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER
     );
 
     /**
@@ -1988,6 +1997,23 @@
     }
 
     /**
+     * Set whether satellite attach for carrier is enabled or disabled by user.
+     *
+     * @param subId Subscription id.
+     * @param isSatelliteAttachEnabledForCarrier Whether satellite attach for carrier is enabled or
+     * disabled.
+     *
+     * @throws IllegalArgumentException if the subscription does not exist.
+     */
+    public void setSatelliteAttachEnabledForCarrier(int subId,
+            int isSatelliteAttachEnabledForCarrier) {
+        writeDatabaseAndCacheHelper(subId,
+                SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
+                isSatelliteAttachEnabledForCarrier,
+                SubscriptionInfoInternal.Builder::setSatelliteAttachEnabledForCarrier);
+    }
+
+    /**
      * Set whether group of the subscription is disabled. This is only useful if it's a grouped
      * opportunistic subscription. In this case, if all primary (non-opportunistic)
      * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
@@ -2243,7 +2269,10 @@
                 .setUserId(cursor.getInt(cursor.getColumnIndexOrThrow(
                         SimInfo.COLUMN_USER_HANDLE)))
                 .setSatelliteEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
-                        SimInfo.COLUMN_SATELLITE_ENABLED)));
+                        SimInfo.COLUMN_SATELLITE_ENABLED)))
+                .setSatelliteAttachEnabledForCarrier(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER)));
         return builder.build();
     }
 
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
index b917698..4eda949 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
@@ -442,6 +442,12 @@
      */
     private final int mIsSatelliteEnabled;
 
+    /**
+     * Whether satellite attach for carrier is enabled or disabled by user.
+     * By default, its disabled. It is intended to use integer to fit the database format.
+     */
+    private final int mIsSatelliteAttachEnabledForCarrier;
+
     // Below are the fields that do not exist in the SimInfo table.
     /**
      * The card ID of the SIM card. This maps uniquely to {@link #mCardString}.
@@ -524,6 +530,8 @@
         this.mLastUsedTPMessageReference = builder.mLastUsedTPMessageReference;
         this.mUserId = builder.mUserId;
         this.mIsSatelliteEnabled = builder.mIsSatelliteEnabled;
+        this.mIsSatelliteAttachEnabledForCarrier =
+                builder.mIsSatelliteAttachEnabledForCarrier;
 
         // Below are the fields that do not exist in the SimInfo table.
         this.mCardId = builder.mCardId;
@@ -1128,6 +1136,13 @@
         return mIsSatelliteEnabled;
     }
 
+    /**
+     * @return {@code 1} if satellite attach for carrier is enabled by user.
+     */
+    public int getSatelliteAttachEnabledForCarrier() {
+        return mIsSatelliteAttachEnabledForCarrier;
+    }
+
     // Below are the fields that do not exist in SimInfo table.
     /**
      * @return The card ID of the SIM card which contains the subscription.
@@ -1253,6 +1268,7 @@
                 + " numberFromIms=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumberFromIms)
                 + " userId=" + mUserId
                 + " isSatelliteEnabled=" + mIsSatelliteEnabled
+                + " satellite_attach_enabled_for_carrier=" + mIsSatelliteAttachEnabledForCarrier
                 + " isGroupDisabled=" + mIsGroupDisabled
                 + "]";
     }
@@ -1308,7 +1324,8 @@
                 mRcsConfig, that.mRcsConfig) && mAllowedNetworkTypesForReasons.equals(
                 that.mAllowedNetworkTypesForReasons) && mDeviceToDeviceStatusSharingContacts.equals(
                 that.mDeviceToDeviceStatusSharingContacts) && mNumberFromCarrier.equals(
-                that.mNumberFromCarrier) && mNumberFromIms.equals(that.mNumberFromIms);
+                that.mNumberFromCarrier) && mNumberFromIms.equals(that.mNumberFromIms)
+                && mIsSatelliteAttachEnabledForCarrier == that.mIsSatelliteAttachEnabledForCarrier;
     }
 
     @Override
@@ -1329,7 +1346,8 @@
                 mDeviceToDeviceStatusSharingContacts, mIsNrAdvancedCallingEnabled,
                 mNumberFromCarrier,
                 mNumberFromIms, mPortIndex, mUsageSetting, mLastUsedTPMessageReference, mUserId,
-                mIsSatelliteEnabled, mCardId, mIsGroupDisabled);
+                mIsSatelliteEnabled, mCardId, mIsGroupDisabled,
+                mIsSatelliteAttachEnabledForCarrier);
         result = 31 * result + Arrays.hashCode(mNativeAccessRules);
         result = 31 * result + Arrays.hashCode(mCarrierConfigAccessRules);
         result = 31 * result + Arrays.hashCode(mRcsConfig);
@@ -1692,6 +1710,11 @@
          */
         private int mIsSatelliteEnabled = -1;
 
+        /**
+         * Whether satellite attach for carrier is enabled by user.
+         */
+        private int mIsSatelliteAttachEnabledForCarrier = -1;
+
         // The following fields do not exist in the SimInfo table.
         /**
          * The card ID of the SIM card which contains the subscription.
@@ -1779,6 +1802,7 @@
             mLastUsedTPMessageReference = info.getLastUsedTPMessageReference();
             mUserId = info.mUserId;
             mIsSatelliteEnabled = info.mIsSatelliteEnabled;
+            mIsSatelliteAttachEnabledForCarrier = info.mIsSatelliteAttachEnabledForCarrier;
             // Below are the fields that do not exist in the SimInfo table.
             mCardId = info.mCardId;
             mIsGroupDisabled = info.mIsGroupDisabled;
@@ -2649,6 +2673,19 @@
             return this;
         }
 
+        /**
+         * Set whether satellite attach for carrier is enabled or disabled by user.
+         * @param isSatelliteAttachEnabledForCarrier {@code 1} if satellite attach for carrier is
+         * enabled.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setSatelliteAttachEnabledForCarrier(
+                @NonNull int isSatelliteAttachEnabledForCarrier) {
+            mIsSatelliteAttachEnabledForCarrier = isSatelliteAttachEnabledForCarrier;
+            return this;
+        }
+
         // Below are the fields that do not exist in the SimInfo table.
         /**
          * Set the card ID of the SIM card which contains the subscription.
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index ba69d8a..3b03a62 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -95,6 +95,7 @@
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.data.PhoneSwitcher;
 import com.android.internal.telephony.euicc.EuiccController;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionDatabaseManager.SubscriptionDatabaseManagerCallback;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.IccUtils;
@@ -173,7 +174,8 @@
             SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
             SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
             SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
-            SimInfo.COLUMN_SATELLITE_ENABLED
+            SimInfo.COLUMN_SATELLITE_ENABLED,
+            SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER
     );
 
     /**
@@ -193,6 +195,10 @@
     @NonNull
     private final Context mContext;
 
+    /** Feature flags */
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     /** App Ops manager instance. */
     @NonNull
     private final AppOpsManager mAppOpsManager;
@@ -405,10 +411,12 @@
      * @param context The context
      * @param looper The looper for the handler.
      */
-    public SubscriptionManagerService(@NonNull Context context, @NonNull Looper looper) {
+    public SubscriptionManagerService(@NonNull Context context, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags) {
         logl("Created SubscriptionManagerService");
         sInstance = this;
         mContext = context;
+        mFeatureFlags = featureFlags;
         mTelephonyManager = context.getSystemService(TelephonyManager.class);
         mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
         mEuiccManager = context.getSystemService(EuiccManager.class);
@@ -1425,6 +1433,9 @@
                         loge("updateSubscription: ICC card is not available.");
                     }
 
+                    // Clear the cached Ims phone number before proceeding with Ims Registration
+                    setNumberFromIms(subId, new String(""));
+
                     // Attempt to restore SIM specific settings when SIM is loaded.
                     Bundle result = mContext.getContentResolver().call(
                             SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
@@ -3590,7 +3601,6 @@
             }
 
             UserHandle userHandle = UserHandle.of(subInfo.getUserId());
-            logv("getSubscriptionUserHandle subId = " + subId + " userHandle = " + userHandle);
             if (userHandle.getIdentifier() == UserHandle.USER_NULL) {
                 return null;
             }
@@ -3977,15 +3987,6 @@
     }
 
     /**
-     * Log verbose messages.
-     *
-     * @param s verbose messages
-     */
-    private void logv(@NonNull String s) {
-        Rlog.v(LOG_TAG, s);
-    }
-
-    /**
      * Dump the state of {@link SubscriptionManagerService}.
      *
      * @param fd File descriptor
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 9456467..a7382ef 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -603,7 +603,7 @@
                     if (phoneId == 0) {
                         if (DBG) {
                             log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON for phoneId 0, "
-                                    + "calling getIccSlotsStatus");
+                                    + "calling getSimSlotsStatus");
                         }
                         mRadioConfig.getSimSlotsStatus(obtainMessage(EVENT_GET_SLOT_STATUS_DONE,
                                 phoneId));
@@ -997,6 +997,12 @@
                             return;
                         }
 
+                        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+                            Rlog.e(LOG_TAG, "updateSimState: Cannot update carrier services. "
+                                    + "Invalid phone id " + phoneId);
+                            return;
+                        }
+
                         // At this point, the SIM state must be a final state (meaning we won't
                         // get more SIM state updates). So resolve the carrier id and update the
                         // carrier services.
@@ -1039,11 +1045,6 @@
             slotId = index;
         }
 
-        if (!mCis[0].supportsEid()) {
-            // we will never get EID from the HAL, so set mDefaultEuiccCardId to UNSUPPORTED_CARD_ID
-            if (DBG) log("eid is not supported");
-            mDefaultEuiccCardId = UNSUPPORTED_CARD_ID;
-        }
         mPhoneIdToSlotId[index] = slotId;
 
         if (VDBG) logPhoneIdToSlotIdMapping();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index dfb91a5..e39c908 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.CarrierServiceStateTracker.ACTION_NEVER_ASK_AGAIN;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.any;
@@ -26,12 +28,14 @@
 import static org.mockito.Mockito.isA;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
@@ -271,4 +275,40 @@
         verify(mNotificationManager, never()).cancel(
                 CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG, SUB_ID);
     }
+
+    /**
+     * Verify the WIFI emergency calling notification is silenced if the user requests (via a
+     * simulated notification action)
+     */
+    @Test
+    @SmallTest
+    public void testEmergencyNotificationBehaviorWhenSilenced() {
+        logd(LOG_TAG + ":testEmergencyNotificationBehaviorWhenSilenced()");
+        sendMessageOnHandler(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
+
+        // verify the notification was sent
+        verify(mNotificationManager, times(1)).notify(
+                eq(CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG),
+                eq(SUB_ID), isA(Notification.class));
+
+        // simulate the user clicking the "Do Not Show Again" button on the notification
+        mCarrierSST.mActionReceiver.onReceive(mContext, new Intent(ACTION_NEVER_ASK_AGAIN));
+
+        // resend the msg to trigger the notification to be posted
+        sendMessageOnHandler(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
+
+        // verify the notification was sent
+        verify(mNotificationManager, times(1)).notify(
+                eq(CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG),
+                eq(SUB_ID), isA(Notification.class));
+    }
+
+    private void sendMessageOnHandler(int messageWhat) {
+        Message notificationMsg = mSpyCarrierSST.obtainMessage(messageWhat, null);
+        doReturn(true).when(mSpyCarrierSST).evaluateSendingMessage(any());
+        doReturn(0).when(mSpyCarrierSST).getDelay(any());
+        doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
+        mSpyCarrierSST.handleMessage(notificationMsg);
+        processAllMessages();
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java
index 40be490..21a14b2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java
@@ -42,6 +42,7 @@
 import android.testing.TestableLooper;
 
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 
 import org.junit.After;
@@ -49,6 +50,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -60,13 +62,15 @@
 
     private CommandsInterface mSpyCi;
     private CellBroadcastConfigTracker mTracker;
+    @Mock private FeatureFlags mFeatureFlags;
 
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         mSpyCi = spy(mSimulatedCommands);
         mPhone = new GsmCdmaPhone(mContext, mSpyCi, mNotifier, true, 0,
-            PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
+            PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager,
+                mFeatureFlags);
         mTracker = CellBroadcastConfigTracker.make(mPhone, mPhone, true);
         mPhone.mCellBroadcastConfigTracker = mTracker;
         processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
index 8cd5dc3..85ea855 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
@@ -39,6 +39,7 @@
 import android.telephony.NetworkServiceCallback;
 import android.telephony.NrVopsSupportInfo;
 import android.telephony.ServiceState;
+import android.telephony.SmsManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.VopsSupportInfo;
@@ -92,6 +93,9 @@
         int dds = SubscriptionManager.getDefaultDataSubscriptionId();
         doReturn(dds).when(mPhone).getSubId();
 
+        mContextFixture.getCarrierConfigBundle().putBoolean(
+                SmsManager.MMS_CONFIG_MMS_ENABLED, false);
+
         logd("CellularNetworkServiceTest -Setup!");
     }
 
@@ -510,4 +514,48 @@
                         new CellIdentityWcdma(),
                         mPhone.getCarrierId()));
     }
+
+    @Test
+    public void testGetAvailableServices_withMmsEnabled() {
+        mContextFixture.getCarrierConfigBundle().putBoolean(
+                SmsManager.MMS_CONFIG_MMS_ENABLED, true);
+
+        VopsSupportInfo lteVopsSupportInfo =
+                new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
+                        LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
+        int voiceRegState = NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+        int voiceRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_UMTS;
+        int reasonForDenial = 0;
+        int maxDataCalls = 4;
+
+        mSimulatedCommands.setVoiceRegState(voiceRegState);
+        mSimulatedCommands.setVoiceRadioTech(voiceRadioTech);
+        mSimulatedCommands.mReasonForDenial = reasonForDenial;
+        mSimulatedCommands.mMaxDataCalls = maxDataCalls;
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        int domain = NetworkRegistrationInfo.DOMAIN_PS;
+        List<Integer> availableServices = Arrays.asList(
+                NetworkRegistrationInfo.SERVICE_TYPE_DATA,
+                NetworkRegistrationInfo.SERVICE_TYPE_MMS);
+        try {
+            mBinder.requestNetworkRegistrationInfo(0, domain, mCallback);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        NetworkRegistrationInfo expectedState = new NetworkRegistrationInfo(
+                domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, voiceRegState,
+                ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
+                false, availableServices, null, "", maxDataCalls,
+                false, false, false, lteVopsSupportInfo);
+
+        try {
+            verify(mCallback, timeout(1000).times(1))
+                    .onRequestNetworkRegistrationInfoComplete(
+                            eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
index 2f4182a..827511d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -96,15 +96,30 @@
     public void testNotifyDataActivity() throws Exception {
         //mock data activity state
         doReturn(TelephonyManager.DATA_ACTIVITY_NONE).when(mPhone).getDataActivityState();
+        doReturn(PHONE_ID).when(mPhone).getPhoneId();
         mDefaultPhoneNotifierUT.notifyDataActivity(mPhone);
-        verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(0),
+        verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(1), eq(0),
                 eq(TelephonyManager.DATA_ACTIVITY_NONE));
 
-        doReturn(1).when(mPhone).getSubId();
+        doReturn(1/*subId*/).when(mPhone).getSubId();
         doReturn(TelephonyManager.DATA_ACTIVITY_IN).when(mPhone).getDataActivityState();
         mDefaultPhoneNotifierUT.notifyDataActivity(mPhone);
-        verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(1),
+        verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(1), eq(1),
                 eq(TelephonyManager.DATA_ACTIVITY_IN));
+
+        doReturn(SUB_ID).when(mPhone).getSubId();
+        doReturn(TelephonyManager.DATA_ACTIVITY_NONE).when(mPhone).getDataActivityState();
+        doReturn(2/*phoneId*/).when(mPhone).getPhoneId();
+        mDefaultPhoneNotifierUT.notifyDataActivity(mPhone);
+        verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(2), eq(0),
+                eq(TelephonyManager.DATA_ACTIVITY_NONE));
+
+        doReturn(1/*subId*/).when(mPhone).getSubId();
+        doReturn(TelephonyManager.DATA_ACTIVITY_INOUT).when(mPhone).getDataActivityState();
+        mDefaultPhoneNotifierUT.notifyDataActivity(mPhone);
+        verify(mTelephonyRegistryManager).notifyDataActivityChanged(
+                eq(2), eq(1), eq(TelephonyManager.DATA_ACTIVITY_INOUT));
+
     }
 
     @Test @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index 3be8509..47e315d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -127,7 +127,10 @@
                     + "," + Telephony.SimInfo.COLUMN_TP_MESSAGE_REF
                     + "  INTEGER DEFAULT -1,"
                     + Telephony.SimInfo.COLUMN_USER_HANDLE + " INTEGER DEFAULT "
-                    + UserHandle.USER_NULL
+                    + UserHandle.USER_NULL + ","
+                    + Telephony.SimInfo.COLUMN_SATELLITE_ENABLED + " INTEGER DEFAULT -1,"
+                    + Telephony.SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER
+                    + " INTEGER DEFAULT -1"
                     + ");";
         }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 465880a..1143190 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -89,6 +89,7 @@
 
 import com.android.internal.telephony.domainselection.DomainSelectionResolver;
 import com.android.internal.telephony.emergency.EmergencyStateTracker;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
@@ -113,6 +114,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
+import org.mockito.Mock;
 import org.mockito.Mockito;
 
 import java.util.ArrayList;
@@ -138,6 +140,7 @@
     // app is not currently debuggable. For now, we use the real device config and ensure that
     // we reset the cellular_security namespace property to its pre-test value after every test.
     private DeviceConfig.Properties mPreTestProperties;
+    @Mock private FeatureFlags mFeatureFlags;
 
     private static final int EVENT_EMERGENCY_CALLBACK_MODE_EXIT = 1;
     private static final int EVENT_EMERGENCY_CALL_TOGGLE = 2;
@@ -176,7 +179,8 @@
         DomainSelectionResolver.setDomainSelectionResolver(mDomainSelectionResolver);
 
         mPhoneUT = new GsmCdmaPhone(mContext, mSimulatedCommands, mNotifier, true, 0,
-            PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
+            PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager,
+                mFeatureFlags);
         mPhoneUT.setVoiceCallSessionStats(mVoiceCallSessionStats);
         ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mUiccController).registerForIccChanged(eq(mPhoneUT), integerArgumentCaptor.capture(),
@@ -981,9 +985,6 @@
         verify(mSimulatedCommandsVerifier).getBasebandVersion(nullable(Message.class));
         verify(mSimulatedCommandsVerifier).getDeviceIdentity(nullable(Message.class));
         verify(mSimulatedCommandsVerifier).getRadioCapability(nullable(Message.class));
-        // once as part of constructor, and once on radio available
-        verify(mSimulatedCommandsVerifier, times(2)).startLceService(anyInt(), anyBoolean(),
-                nullable(Message.class));
 
         // EVENT_RADIO_ON
         verify(mSimulatedCommandsVerifier).getVoiceRadioTechnology(nullable(Message.class));
@@ -1006,8 +1007,6 @@
         // EVENT_RADIO_AVAILABLE
         verify(mSimulatedCommandsVerifier, times(2)).getBasebandVersion(nullable(Message.class));
         verify(mSimulatedCommandsVerifier, times(2)).getDeviceIdentity(nullable(Message.class));
-        verify(mSimulatedCommandsVerifier, times(3)).startLceService(anyInt(), anyBoolean(),
-                nullable(Message.class));
 
         // EVENT_RADIO_ON
         verify(mSimulatedCommandsVerifier, times(2)).getVoiceRadioTechnology(
@@ -1040,7 +1039,8 @@
         };
 
         Phone phone = new GsmCdmaPhone(mContext, sc, mNotifier, true, 0,
-                PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
+                PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager,
+                mFeatureFlags);
         phone.setVoiceCallSessionStats(mVoiceCallSessionStats);
         ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mUiccController).registerForIccChanged(eq(phone), integerArgumentCaptor.capture(),
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
index 4538dea..02780bd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import android.os.Parcel;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
@@ -30,6 +31,7 @@
 import org.junit.Test;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /** Unit tests for {@link NetworkScanRequest}. */
 public class NetworkScanRequestTest {
@@ -37,6 +39,137 @@
     @Test
     @SmallTest
     public void testParcel() {
+        NetworkScanRequest nsq = createNetworkScanRequest();
+
+        Parcel p = Parcel.obtain();
+        nsq.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        NetworkScanRequest newNsq = NetworkScanRequest.CREATOR.createFromParcel(p);
+        assertEquals(nsq, newNsq);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_identity_allFieldsNonNull() {
+        NetworkScanRequest nsq = createNetworkScanRequest();
+
+        assertEquals(nsq, nsq);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_identify_nullRadioAccessSpecifiers() {
+        NetworkScanRequest nsq = createNetworkScanRequest(null, List.of("310480"));
+
+        assertEquals(nsq, nsq);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_identify_emptyRadioAccessSpecifiers() {
+        NetworkScanRequest nsq = createNetworkScanRequest(new RadioAccessSpecifier[]{},
+                List.of("310480"));
+
+        assertEquals(nsq, nsq);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_identify_nullPlmns() {
+        NetworkScanRequest nsq = createNetworkScanRequest(new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkType.GERAN, null, null)}, null);
+
+        assertEquals(nsq, nsq);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_identify_emptyPlmns() {
+        NetworkScanRequest nsq = createNetworkScanRequest(new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkType.GERAN, null, null)}, List.of());
+
+        assertEquals(nsq, nsq);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_identify_nullRasAndPlmns() {
+        NetworkScanRequest nsq = createNetworkScanRequest(null, null);
+
+        assertEquals(nsq, nsq);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_sameValues_allFieldsNonNull() {
+        NetworkScanRequest nsq1 = createNetworkScanRequest();
+        NetworkScanRequest nsq2 = createNetworkScanRequest();
+
+        assertEquals(nsq1, nsq2);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_sameValues_nullRadioAccessSpecifiers() {
+        NetworkScanRequest nsq1 = createNetworkScanRequest(null, List.of("310480"));
+        NetworkScanRequest nsq2 = createNetworkScanRequest(null, List.of("310480"));
+
+        assertEquals(nsq1, nsq2);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_sameValues_emptyRadioAccessSpecifiers() {
+        NetworkScanRequest nsq1 = createNetworkScanRequest(new RadioAccessSpecifier[]{},
+                List.of("310480"));
+        NetworkScanRequest nsq2 = createNetworkScanRequest(new RadioAccessSpecifier[]{},
+                List.of("310480"));
+
+        assertEquals(nsq1, nsq2);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_sameValues_nullPlmns() {
+        NetworkScanRequest nsq1 = createNetworkScanRequest(new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkType.GERAN, null, null)}, null);
+        NetworkScanRequest nsq2 = createNetworkScanRequest(new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkType.GERAN, null, null)}, null);
+
+        assertEquals(nsq1, nsq2);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_sameValues_emptyPlmns() {
+        NetworkScanRequest nsq1 = createNetworkScanRequest(new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkType.GERAN, null, null)}, List.of());
+        NetworkScanRequest nsq2 = createNetworkScanRequest(new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkType.GERAN, null, null)}, List.of());
+
+        assertEquals(nsq1, nsq2);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_sameValues_nullRasAndPlmns() {
+        NetworkScanRequest nsq1 = createNetworkScanRequest(null, null);
+        NetworkScanRequest nsq2 = createNetworkScanRequest(null, null);
+
+        assertEquals(nsq1, nsq2);
+    }
+
+    @Test
+    @SmallTest
+    public void testEquals_plmnsInDifferentOrder_shouldNotEqual() {
+        NetworkScanRequest nsq1 = createNetworkScanRequest(null, List.of("123456", "987654"));
+        NetworkScanRequest nsq2 = createNetworkScanRequest(null, List.of("987654", "123456"));
+
+        assertNotEquals(nsq1, nsq2);
+    }
+
+    private NetworkScanRequest createNetworkScanRequest() {
         int ranGsm = AccessNetworkType.GERAN;
         int[] gsmBands = {GeranBand.BAND_T380, GeranBand.BAND_T410};
         int[] gsmChannels = {1, 2, 3, 4};
@@ -46,22 +179,30 @@
         int[] lteChannels = {5, 6, 7, 8};
         RadioAccessSpecifier lte = new RadioAccessSpecifier(ranLte, lteBands, lteChannels);
         RadioAccessSpecifier[] ras = {gsm, lte};
+
         int searchPeriodicity = 70;
         int maxSearchTime = 200;
         boolean incrementalResults = true;
         int incrementalResultsPeriodicity = 7;
+
         ArrayList<String> mccmncs = new ArrayList<String>();
         mccmncs.add("310480");
         mccmncs.add("21002");
-        NetworkScanRequest nsq = new NetworkScanRequest(NetworkScanRequest.SCAN_TYPE_ONE_SHOT, ras,
-                  searchPeriodicity, maxSearchTime, incrementalResults,
-                  incrementalResultsPeriodicity, mccmncs);
 
-        Parcel p = Parcel.obtain();
-        nsq.writeToParcel(p, 0);
-        p.setDataPosition(0);
+        return new NetworkScanRequest(NetworkScanRequest.SCAN_TYPE_ONE_SHOT, ras,
+                searchPeriodicity, maxSearchTime, incrementalResults,
+                incrementalResultsPeriodicity, mccmncs);
+    }
 
-        NetworkScanRequest newNsq = NetworkScanRequest.CREATOR.createFromParcel(p);
-        assertEquals(nsq, newNsq);
+    private NetworkScanRequest createNetworkScanRequest(
+            RadioAccessSpecifier[] radioAccessSpecifiers, List<String> plmns) {
+        int searchPeriodicity = 70;
+        int maxSearchTime = 200;
+        boolean incrementalResults = true;
+        int incrementalResultsPeriodicity = 7;
+
+        return new NetworkScanRequest(NetworkScanRequest.SCAN_TYPE_ONE_SHOT, radioAccessSpecifiers,
+                searchPeriodicity, maxSearchTime, incrementalResults,
+                incrementalResultsPeriodicity, plmns != null ? new ArrayList<>(plmns) : null);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTrackerTest.java
new file mode 100644
index 0000000..f0ffe34
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTrackerTest.java
@@ -0,0 +1,924 @@
+/*
+ * 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.internal.telephony;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.nullable;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.pm.PackageManager;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.UserHandle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CellInfoLte;
+import android.telephony.NetworkScan;
+import android.telephony.NetworkScanRequest;
+import android.telephony.RadioAccessSpecifier;
+import android.telephony.TelephonyScanManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import com.android.internal.telephony.NetworkScanRequestTracker.NetworkScanRequestInfo;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit test for NetworkScanRequestTracker.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NetworkScanRequestTrackerTest extends TelephonyTest {
+    private static final String TAG = "NetworkScanRequestTrackerTest";
+
+    private static final String CLIENT_PKG = "com.android.testapp";
+    private static final int CLIENT_UID = 123456;
+
+    // Keep the same as in NetworkScanRequestTracker.
+    // This is the only internal implementation that the UT has to depend on
+    // in order to fully emulate NetworkScanResult in various cases
+    private static final int EVENT_RECEIVE_NETWORK_SCAN_RESULT = 3;
+
+    // Mocks
+    private CommandsInterface mMockCi;
+
+    private NetworkScanRequestTracker mNetworkScanRequestTracker;
+    private HandlerThread mTestHandlerThread;
+    private Handler mHandler;
+    private int mScanId;
+    private List<Message> mMessages = new ArrayList<>();
+    // Latch to make sure all messages are received before verifying.
+    // Default count is 1 but can override to match expected msg number
+    private CountDownLatch mMessageLatch = new CountDownLatch(1);
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+
+        mMockCi = Mockito.mock(CommandsInterface.class);
+        mPhone.mCi = mMockCi;
+
+        mTestHandlerThread = new HandlerThread(TAG);
+        mTestHandlerThread.start();
+        mHandler = new Handler(mTestHandlerThread.getLooper()) {
+            @Override
+            public void handleMessage(Message message) {
+                Log.d(TAG, "Received msg: " + message);
+                mMessages.add(Message.obtain(message));
+                mMessageLatch.countDown();
+            }
+        };
+
+        mNetworkScanRequestTracker = new NetworkScanRequestTracker();
+        mScanId = TelephonyScanManager.INVALID_SCAN_ID;
+        setHasLocationPermissions(false);
+        assertThat(mHandler).isNotNull();
+        processAllMessages();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        stopNetworkScanIfNeeded(mScanId);
+        mTestHandlerThread.quit();
+        mMessages.clear();
+        super.tearDown();
+    }
+
+    @Test
+    public void testStartNetworkScan_nullRequest_shouldNeverScan() throws Exception {
+        NetworkScanRequest request = null;
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan when the request is null!"))
+                .startNetworkScan(any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestWithNullSpecifier_shouldNeverScan() throws Exception {
+        RadioAccessSpecifier[] specifiers = null;
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers,
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan when no RadioAccessSpecifier is provided!"))
+                .startNetworkScan(any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestWithEmptySpecifier_shouldNeverScan() throws Exception {
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{};
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers,
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan when empty RadioAccessSpecifier is "
+                        + "provided!")).startNetworkScan(
+                any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestWithTooManySpecifiers_shouldNeverScan()
+            throws Exception {
+        // More than NetworkScanRequest.MAX_RADIO_ACCESS_NETWORKS (8)
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        new int[]{AccessNetworkConstants.GeranBand.BAND_T380}, null),
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        new int[]{AccessNetworkConstants.GeranBand.BAND_T410}, null),
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        new int[]{AccessNetworkConstants.GeranBand.BAND_450}, null),
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        new int[]{AccessNetworkConstants.GeranBand.BAND_480}, null),
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        new int[]{AccessNetworkConstants.GeranBand.BAND_710}, null),
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        new int[]{AccessNetworkConstants.GeranBand.BAND_750}, null),
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        new int[]{AccessNetworkConstants.GeranBand.BAND_T810}, null),
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        new int[]{AccessNetworkConstants.GeranBand.BAND_850}, null),
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        new int[]{AccessNetworkConstants.GeranBand.BAND_P900}, null),
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers, /* specifiers */
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan when request with too many "
+                        + "RadioAccessSpecifiers!")).startNetworkScan(
+                any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestWithTooManyBands_shouldNeverScan() throws Exception {
+        // More than NetworkScanRequest.MAX_BANDS (8)
+        int[] bands = new int[]{
+                AccessNetworkConstants.GeranBand.BAND_T380,
+                AccessNetworkConstants.GeranBand.BAND_T410,
+                AccessNetworkConstants.GeranBand.BAND_450,
+                AccessNetworkConstants.GeranBand.BAND_480,
+                AccessNetworkConstants.GeranBand.BAND_710,
+                AccessNetworkConstants.GeranBand.BAND_750,
+                AccessNetworkConstants.GeranBand.BAND_T810,
+                AccessNetworkConstants.GeranBand.BAND_850,
+                AccessNetworkConstants.GeranBand.BAND_P900,
+        };
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        bands, null),
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers, /* specifiers */
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan when request with too many bands!"))
+                .startNetworkScan(any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestWithTooManyChannels_shouldNeverScan() throws Exception {
+        // More than NetworkScanRequest.MAX_CHANNELS (32)
+        int[] channels =
+                new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+                        22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+                };
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        null, channels),
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers, /* specifiers */
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan when request with too many channels!"))
+                .startNetworkScan(any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+
+    }
+
+    @Test
+    public void testStartNetworkScan_requestWithUnsupportedRan_shouldNeverScan() throws Exception {
+        int[] unsupportedRans = new int[]{
+                AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+                AccessNetworkConstants.AccessNetworkType.CDMA2000,
+                AccessNetworkConstants.AccessNetworkType.IWLAN,
+        };
+        for (int ran : unsupportedRans) {
+            RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                    new RadioAccessSpecifier(ran, null, null),
+            };
+            NetworkScanRequest request = new NetworkScanRequest(
+                    NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                    specifiers, /* specifiers */
+                    5 /* searchPeriodicity */,
+                    60 /* maxSearchTime in seconds */,
+                    true /* incrementalResults */,
+                    5 /* incrementalResultsPeriodicity */,
+                    null /* PLMNs */);
+            Messenger messenger = new Messenger(mHandler);
+
+            int scanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                    mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+            processAllMessages();
+
+            verify(mPhone, never().description(
+                    "Phone should never start network scan with unsupported RAN "
+                            + ran + "!")).startNetworkScan(any(), any());
+
+            // Nothing is needed to clean up on success.
+            // This is just for failure cases when startNetworkScan was issued.
+            stopNetworkScanIfNeeded(scanId);
+        }
+    }
+
+    @Test
+    public void testStartNetworkScan_requestWithTooSmallPeriodicity_shouldNeverScan()
+            throws Exception {
+        int searchPeriodicity = NetworkScanRequest.MIN_SEARCH_PERIODICITY_SEC - 1;
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        null, null),
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers, /* specifiers */
+                searchPeriodicity /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan when request with too small periodicity!"))
+                .startNetworkScan(any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestWithTooLargePeriodicity_shouldNeverScan()
+            throws Exception {
+        int searchPeriodicity = NetworkScanRequest.MAX_SEARCH_MAX_SEC + 1;
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        null, null),
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers, /* specifiers */
+                searchPeriodicity /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan with too large periodicity!"))
+                .startNetworkScan(any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestWithTooSmallIncPeriodicity_shouldNeverScan()
+            throws Exception {
+        int incPeriodicity = NetworkScanRequest.MIN_INCREMENTAL_PERIODICITY_SEC - 1;
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        null, null),
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers, /* specifiers */
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                incPeriodicity /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan with too small incremental results "
+                        + "periodicity!")).startNetworkScan(
+                any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestWithTooLargeIncPeriodicity_shouldNeverScan()
+            throws Exception {
+        int incPeriodicity = NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC + 1;
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        null, null),
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers, /* specifiers */
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                incPeriodicity /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan with too large incremental periodicity!"))
+                .startNetworkScan(any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestPeriodBiggerThanMax_shouldNeverScan() throws Exception {
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        null, null),
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers, /* specifiers */
+                61 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan when periodicity is larger than max search"
+                        + " time!")).startNetworkScan(
+                any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestIncPeriodBiggerThanMax_shouldNeverScan()
+            throws Exception {
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        null, null),
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers, /* specifiers */
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                61 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan when incremental results periodicity is "
+                        + "larger than max search time!")).startNetworkScan(
+                any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_requestTooManyPlmns_shouldNeverScan() throws Exception {
+        // More than NetworkScanRequest.MAX_MCC_MNC_LIST_SIZE (20) PLMNs
+        List<String> plmns = List.of("11110", "11111", "11112", "11113", "11114", "11115", "11116",
+                "11117", "11118", "11119", "11120", "11121", "11122", "11123", "11124", "11125",
+                "11126", "11127", "11128", "11129", "11130", "11131");
+
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.GERAN,
+                        null, null),
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers, /* specifiers */
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                new ArrayList<>(plmns) /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+
+        mScanId = mNetworkScanRequestTracker.startNetworkScan(true, request, messenger,
+                mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        verify(mPhone, never().description(
+                "Phone should never start network scan when request with too many PLMNs!"))
+                .startNetworkScan(any(), any());
+        verifyMessage(TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN,
+                mScanId, null);
+    }
+
+    @Test
+    public void testStartNetworkScan_succeed_returnValidScanId() throws Exception {
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+
+        verify(mPhone).startNetworkScan(any(), any());
+        assertThat(mScanId).isNotEqualTo(TelephonyScanManager.INVALID_SCAN_ID);
+    }
+
+    @Test
+    public void testStartNetworkScan_succeed_deathRecipientIsLinked() throws Exception {
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+
+        verify(mIBinder).linkToDeath(any(), anyInt());
+    }
+
+    @Test
+    public void testStartNetworkScan_multipleRequests_scanIdShouldNotRepeat() throws Exception {
+        int firstScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+        stopNetworkScanIfNeeded(firstScanId);
+
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+
+        assertThat(mScanId).isNotEqualTo(firstScanId);
+    }
+
+    @Test
+    public void testStartNetworkScan_singleResultAndSuccess_allMessagesNotified() throws Exception {
+        mMessageLatch = new CountDownLatch(2); // 2 messages expected
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+
+        // Only one result and it is completed.
+        verifyStartNetworkScanAndEmulateScanResult(
+                new NetworkScanResult(NetworkScanResult.SCAN_STATUS_COMPLETE,
+                NetworkScan.SUCCESS, List.of(new CellInfoLte())));
+
+        verifyMessages(
+                // One RESTRICTED_SCAN_RESULTS msg follows by COMPLETE msg
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_RESTRICTED_SCAN_RESULTS,
+                        NetworkScan.SUCCESS, mScanId, null),
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_SCAN_COMPLETE,
+                        NetworkScan.SUCCESS, mScanId, null)
+        );
+    }
+
+    @Test
+    public void testStartNetworkScan_resultFromCopiedRequest_noMessagesNotified() throws Exception {
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+
+        // Scan result came but with copied (instead of original) NetworkScanRequestInfo
+        NetworkScanRequestInfo obsoletedNsri = createNetworkScanRequestInfo(mScanId);
+        verifyStartNetworkScanAndEmulateScanResult(obsoletedNsri,
+                new NetworkScanResult(NetworkScanResult.SCAN_STATUS_COMPLETE,
+                        NetworkScan.SUCCESS, List.of(new CellInfoLte())));
+
+        // No message should send to client
+        verifyMessages();
+    }
+
+    @Test
+    public void testStartNetworkScan_multiResultsAndSuccess_allMessagesNotified() throws Exception {
+        mMessageLatch = new CountDownLatch(4); // 4 messages expected
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+
+        verifyStartNetworkScanAndEmulateScanResult(
+                // First two results arrived but did not complete
+                new NetworkScanResult(NetworkScanResult.SCAN_STATUS_PARTIAL,
+                        NetworkScan.SUCCESS, List.of(new CellInfoLte())),
+                new NetworkScanResult(NetworkScanResult.SCAN_STATUS_PARTIAL,
+                        NetworkScan.SUCCESS, List.of(new CellInfoLte())),
+                // Third result arrived and completed
+                new NetworkScanResult(NetworkScanResult.SCAN_STATUS_COMPLETE,
+                        NetworkScan.SUCCESS, List.of(new CellInfoLte())));
+
+        verifyMessages(
+                // Same number of SCAN_RESULTS and end with COMPLETE messages.
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_RESTRICTED_SCAN_RESULTS,
+                        NetworkScan.SUCCESS, mScanId, null),
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_RESTRICTED_SCAN_RESULTS,
+                        NetworkScan.SUCCESS, mScanId, null),
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_RESTRICTED_SCAN_RESULTS,
+                        NetworkScan.SUCCESS, mScanId, null),
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_SCAN_COMPLETE,
+                        NetworkScan.SUCCESS, mScanId, null)
+        );
+    }
+
+    @Test
+    public void testStartNetworkScan_modemError_allMessagesNotified() throws Exception {
+        mMessageLatch = new CountDownLatch(3); // 3 messages expected
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+
+        verifyStartNetworkScanAndEmulateScanResult(
+                // First result arrived but did not complete
+                new NetworkScanResult(NetworkScanResult.SCAN_STATUS_PARTIAL,
+                        NetworkScan.SUCCESS, List.of(new CellInfoLte())),
+                // Final result arrived and indicated modem error
+                new NetworkScanResult(NetworkScanResult.SCAN_STATUS_COMPLETE,
+                        NetworkScan.ERROR_MODEM_ERROR, List.of(new CellInfoLte())));
+
+        verifyMessages(
+                // First SUCCESS scan result
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_RESTRICTED_SCAN_RESULTS,
+                        NetworkScan.SUCCESS, mScanId, null),
+                // Final ERROR messages
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_RESTRICTED_SCAN_RESULTS,
+                        NetworkScan.ERROR_MODEM_ERROR, mScanId, null),
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_SCAN_ERROR,
+                        NetworkScan.ERROR_MODEM_ERROR, mScanId, null)
+        );
+    }
+
+    @Test
+    public void testStartNetworkScan_clientDied_shouldStopScan() throws Exception {
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+
+        verifyStartNetworkScanAndEmulateBinderDied();
+
+        verify(mPhone).stopNetworkScan(any());
+    }
+
+    @Test
+    public void testStopNetworkScan_invalidScanId_throwIllegalArgumentException() throws Exception {
+        final int invalidScanId = 1000;
+        assertThrows(IllegalArgumentException.class,
+                () -> mNetworkScanRequestTracker.stopNetworkScan(invalidScanId, CLIENT_UID));
+    }
+
+    @Test
+    public void testStopNetworkScan_fromOtherUid_throwIllegalArgumentException() throws Exception {
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mNetworkScanRequestTracker.stopNetworkScan(mScanId, 654321));
+    }
+
+    @Test
+    public void testStopNetworkScan_scanIdAndUidMatchWithoutError_shouldUnregister()
+            throws Exception {
+        mMessageLatch = new CountDownLatch(1); // 1 message expected
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+        mNetworkScanRequestTracker.stopNetworkScan(mScanId, CLIENT_UID);
+        processAllMessages();
+
+        // No error during stopping network scan
+        verifyStopNetworkScanAndEmulateResult(null /* commandException */);
+
+        verify(mPhone.mCi).unregisterForNetworkScanResult(any());
+        verify(mPhone.mCi).unregisterForModemReset(any());
+        verify(mPhone.mCi).unregisterForNotAvailable(any());
+
+        verifyMessages(
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_SCAN_COMPLETE,
+                        NetworkScan.SUCCESS, mScanId, null)
+        );
+    }
+
+    @Test
+    public void testStopNetworkScan_withError_reportTranslatedScanError()
+            throws Exception {
+        mMessageLatch = new CountDownLatch(1); // 1 message expected
+        mScanId = scanNetworkWithOneShot(true /* renounceFineLocationAccess */);
+        mNetworkScanRequestTracker.stopNetworkScan(mScanId, CLIENT_UID);
+        processAllMessages();
+
+        // No memory error during stopping network scan
+        verifyStopNetworkScanAndEmulateResult(
+                new CommandException(CommandException.Error.NO_MEMORY));
+
+        verifyMessages(
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_SCAN_ERROR,
+                        NetworkScan.ERROR_MODEM_ERROR, mScanId, null)
+        );
+    }
+
+    // -- Test cases below cover scenarios when caller has FINE_LOCATION permission --
+    @Test
+    public void testStartNetworkScan_withLocationPermission_allMessagesNotified()
+            throws Exception {
+        setHasLocationPermissions(true);
+        mMessageLatch = new CountDownLatch(2); // 2 messages expected
+        mScanId = scanNetworkWithOneShot(false /* renounceFineLocationAccess */);
+
+        // Only one result and it is completed.
+        verifyStartNetworkScanAndEmulateScanResult(
+                new NetworkScanResult(NetworkScanResult.SCAN_STATUS_COMPLETE,
+                        NetworkScan.SUCCESS, List.of(new CellInfoLte())));
+
+        verifyMessages(
+                // One SCAN_RESULTS msg follows by COMPLETE msg
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_SCAN_RESULTS,
+                        NetworkScan.SUCCESS, mScanId, null),
+                Message.obtain(mHandler, TelephonyScanManager.CALLBACK_SCAN_COMPLETE,
+                        NetworkScan.SUCCESS, mScanId, null)
+        );
+    }
+
+    private void stopNetworkScanIfNeeded(int scanId) {
+        if (scanId != TelephonyScanManager.INVALID_SCAN_ID) {
+            try {
+                mNetworkScanRequestTracker.stopNetworkScan(mScanId, CLIENT_UID);
+                processAllMessages();
+            } catch (IllegalArgumentException ignored) {
+            }
+        }
+    }
+
+    private void verifyMessages(Message... messages) {
+        try {
+            mMessageLatch.await(5, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new AssertionError("Interrupted while latch is wait for messages");
+        }
+
+        assertThat(mMessages.size()).isEqualTo(messages.length);
+        for (int i = 0; i < messages.length; i++) {
+            // We only care about what/arg1/arg2/obj by now
+            assertThat(mMessages.get(i).what).isEqualTo(messages[i].what);
+            assertThat(mMessages.get(i).arg1).isEqualTo(messages[i].arg1);
+            assertThat(mMessages.get(i).arg2).isEqualTo(messages[i].arg2);
+            assertThat(mMessages.get(i).obj).isEqualTo(messages[i].obj);
+        }
+    }
+
+    /**
+     * Verify only one Message with better readability
+     */
+    private void verifyMessage(int what, int arg1, int arg2, Object obj) {
+        verifyMessages(Message.obtain(mHandler, what, arg1, arg2, obj));
+    }
+
+
+    /**
+     * Verify both {@link CommandsInterface#startNetworkScan(NetworkScanRequest, Message)} and
+     * {@link CommandsInterface#registerForNetworkScanResult(Handler, int, Object)} and sends
+     * emulated {@link NetworkScanResult} with original NetworkScanRequestInfo back through the
+     * captures.
+     */
+    private void verifyStartNetworkScanAndEmulateScanResult(
+            NetworkScanResult... networkScanResults) {
+        NetworkScanRequestInfo nsri = verifyStartNetworkScanAndGetNetworkScanInfo();
+
+        sendEmulatedScanResult(nsri, networkScanResults);
+    }
+
+    /**
+     * Similar as verifyStartNetworkScanAndEmulateScanResult(NetworkScanResult...) above but
+     * allows to set the customized NetworkScanRequestInfo other than original one.
+     */
+    private void verifyStartNetworkScanAndEmulateScanResult(
+            NetworkScanRequestInfo nsri,
+            NetworkScanResult... networkScanResults) {
+        // Ignore the original NSRI returned
+        verifyStartNetworkScanAndGetNetworkScanInfo();
+
+        sendEmulatedScanResult(nsri, networkScanResults);
+    }
+
+    private void sendEmulatedScanResult(NetworkScanRequestInfo nsri,
+            NetworkScanResult... networkScanResults) {
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+        verify(mPhone.mCi).registerForNetworkScanResult(handlerCaptor.capture(), anyInt(), any());
+        Handler handler = handlerCaptor.getValue();
+
+        for (NetworkScanResult networkScanResult : networkScanResults) {
+            Message result = Message.obtain(handler, EVENT_RECEIVE_NETWORK_SCAN_RESULT, nsri);
+            AsyncResult.forMessage(result, networkScanResult, null);
+            result.sendToTarget();
+        }
+        processAllMessages();
+    }
+
+    /**
+     * Verify {@link CommandsInterface#startNetworkScan(NetworkScanRequest, Message)} and emulate
+     * client process died through the captured {@link IBinder.DeathRecipient}.
+     */
+    private void verifyStartNetworkScanAndEmulateBinderDied() {
+        IBinder.DeathRecipient nsri = verifyStartNetworkScanAndGetNetworkScanInfo();
+        nsri.binderDied();
+        processAllMessages();
+    }
+
+    /**
+     * Verify {@link Phone#startNetworkScan(NetworkScanRequest, Message)} and
+     * capture the NetworkScanRequestInfo for further usage.
+     */
+    private NetworkScanRequestInfo verifyStartNetworkScanAndGetNetworkScanInfo() {
+        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mPhone).startNetworkScan(any(), messageCaptor.capture());
+        Message responseMessage = messageCaptor.getValue();
+        // NetworkScanRequestInfo is not public and can only be treated as IBinder.DeathRecipient
+        NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) responseMessage.obj;
+        AsyncResult.forMessage(responseMessage, nsri, null);
+        responseMessage.sendToTarget();
+        processAllMessages();
+        return nsri;
+    }
+
+    /**
+     * Verify {@link  Phone#stopNetworkScan} and emulate the result (succeed or failures)
+     * through the {@code commandException}.
+     */
+    private void verifyStopNetworkScanAndEmulateResult(CommandException commandException) {
+        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mPhone).stopNetworkScan(messageCaptor.capture());
+        Message responseMessage = messageCaptor.getValue();
+        // NetworkScanRequestInfo is not public and can only be treated as Object here
+        Object nsri = responseMessage.obj;
+        AsyncResult.forMessage(responseMessage, nsri, commandException);
+        responseMessage.sendToTarget();
+        processAllMessages();
+    }
+
+    private int scanNetworkWithOneShot(boolean renounceFineLocationAccess) {
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, null,
+                        null)
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers,
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+        int scanId = mNetworkScanRequestTracker.startNetworkScan(renounceFineLocationAccess,
+                request, messenger, mIBinder, mPhone, CLIENT_UID, -1, CLIENT_PKG);
+        processAllMessages();
+
+        return scanId;
+    }
+
+    private NetworkScanRequestInfo createNetworkScanRequestInfo(int scanId) {
+        RadioAccessSpecifier[] specifiers = new RadioAccessSpecifier[]{
+                new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, null,
+                        null)
+        };
+        NetworkScanRequest request = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
+                specifiers,
+                5 /* searchPeriodicity */,
+                60 /* maxSearchTime in seconds */,
+                true /* incrementalResults */,
+                5 /* incrementalResultsPeriodicity */,
+                null /* PLMNs */);
+        Messenger messenger = new Messenger(mHandler);
+        return mNetworkScanRequestTracker.new NetworkScanRequestInfo(request, messenger, mIBinder,
+                scanId, mPhone, CLIENT_UID, -1, CLIENT_PKG, true);
+    }
+
+    private void setHasLocationPermissions(boolean hasPermission) {
+        if (!hasPermission) {
+            // System location off, LocationAccessPolicy#checkLocationPermission returns DENIED_SOFT
+            when(mLocationManager.isLocationEnabledForUser(any(UserHandle.class)))
+                    .thenReturn(false);
+        } else {
+            // Turn on all to let LocationAccessPolicy#checkLocationPermission returns ALLOWED
+            when(mContext.checkPermission(eq(Manifest.permission.ACCESS_FINE_LOCATION),
+                    anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+            when(mContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
+                    anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+            when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OPSTR_FINE_LOCATION),
+                    anyInt(), anyString(), nullable(String.class), nullable(String.class)))
+                    .thenReturn(AppOpsManager.MODE_ALLOWED);
+            when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OPSTR_COARSE_LOCATION),
+                    anyInt(), anyString(), nullable(String.class), nullable(String.class)))
+                    .thenReturn(AppOpsManager.MODE_ALLOWED);
+            when(mLocationManager.isLocationEnabledForUser(any(UserHandle.class))).thenReturn(true);
+            when(mContext.checkPermission(eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL),
+                    anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+        }
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index 2f3bbf7..3c7e0b6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -216,6 +216,12 @@
         assertNull(PhoneNumberUtils.toCallerIDMinMatch(null));
         assertNull(PhoneNumberUtils.getStrippedReversed(null));
         assertNull(PhoneNumberUtils.stringFromStringAndTOA(null, 1));
+
+        // Test for known potential bad extraction of post dial portion.
+        assertEquals(";123456",
+                PhoneNumberUtils.extractPostDialPortion("6281769222;123456"));
+        assertEquals("6281769222", PhoneNumberUtils.extractNetworkPortion(
+                "6281769222;123456"));
     }
 
     @SmallTest
@@ -856,4 +862,20 @@
         assertEquals(TtsSpan.TYPE_TELEPHONE, ttsSpan.getType());
         assertEquals(expected, ttsSpan.getArgs().getString(TtsSpan.ARG_NUMBER_PARTS));
     }
+
+    @SmallTest
+    @Test
+    public void testWpsCallNumber() {
+        // Test number without special symbols.
+        assertFalse(PhoneNumberUtils.isWpsCallNumber("12345678"));
+
+        // TTS number should not be recognized as wps.
+        assertFalse(PhoneNumberUtils.isWpsCallNumber("*23212345678"));
+        assertFalse(PhoneNumberUtils.isWpsCallNumber("*232#12345678"));
+
+        // Check WPS valid numbers
+        assertTrue(PhoneNumberUtils.isWpsCallNumber("*27212345678"));
+        assertTrue(PhoneNumberUtils.isWpsCallNumber("*31#*27212345678"));
+        assertTrue(PhoneNumberUtils.isWpsCallNumber("#31#*27212345678"));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index 2396d1d..bfe9649 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -64,7 +64,6 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_NV_RESET_CONFIG;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_NV_WRITE_ITEM;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_OPERATOR;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_PULL_LCEDATA;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_REPORT_SMS_MEMORY_STATUS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING;
@@ -82,10 +81,8 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_AUTHENTICATION;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_CLOSE_CHANNEL;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_OPEN_CHANNEL;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_LCE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_NETWORK_SCAN;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_LCE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UDUB;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_RADIO_TECH;
@@ -105,17 +102,18 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.hardware.radio.V1_0.Carrier;
 import android.hardware.radio.V1_0.CdmaSmsMessage;
-import android.hardware.radio.V1_0.DataProfileInfo;
 import android.hardware.radio.V1_0.GsmSmsMessage;
 import android.hardware.radio.V1_0.ImsSmsMessage;
 import android.hardware.radio.V1_0.NvWriteItem;
@@ -130,6 +128,7 @@
 import android.net.LinkAddress;
 import android.os.AsyncResult;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IPowerManager;
 import android.os.IThermalService;
 import android.os.Looper;
@@ -139,6 +138,7 @@
 import android.os.WorkSource;
 import android.service.carrier.CarrierIdentifier;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.CellConfigLte;
 import android.telephony.CellIdentityCdma;
 import android.telephony.CellIdentityGsm;
 import android.telephony.CellIdentityLte;
@@ -158,11 +158,10 @@
 import android.telephony.CellSignalStrengthNr;
 import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
+import android.telephony.ClosedSubscriberGroupInfo;
 import android.telephony.NetworkScanRequest;
 import android.telephony.RadioAccessFamily;
 import android.telephony.RadioAccessSpecifier;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
 import android.telephony.SmsManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
@@ -199,6 +198,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
 @RunWith(AndroidTestingRunner.class)
@@ -220,13 +221,10 @@
     private RadioSimProxy mSimProxy;
     private RadioModemProxy mRadioModemProxy;
 
-    private Map<Integer, HalVersion> mHalVersionV10 = new HashMap<>();
-    private Map<Integer, HalVersion> mHalVersionV11 = new HashMap<>();
-    private Map<Integer, HalVersion> mHalVersionV12 = new HashMap<>();
-    private Map<Integer, HalVersion> mHalVersionV13 = new HashMap<>();
     private Map<Integer, HalVersion> mHalVersionV14 = new HashMap<>();
     private Map<Integer, HalVersion> mHalVersionV15 = new HashMap<>();
     private Map<Integer, HalVersion> mHalVersionV16 = new HashMap<>();
+    private Map<Integer, HalVersion> mHalVersionV20 = new HashMap<>();
     private Map<Integer, HalVersion> mHalVersionV21 = new HashMap<>();
 
     private RIL mRILInstance;
@@ -243,25 +241,21 @@
     private static final int CI = 268435456;
     private static final int CID = 65535;
     private static final int CQI = 2147483647;
+    private static final int CQI_TABLE_INDEX = 1;
     private static final int DBM = -74;
     private static final int EARFCN = 262140;
-    private static final List<Integer> BANDS = Arrays.asList(1, 2);
+    private static final ArrayList<Integer> BANDS = new ArrayList<>(Arrays.asList(1, 2));
     private static final int BANDWIDTH = 5000;
     private static final int ECIO = -124;
-    private static final String EMPTY_ALPHA_LONG = "";
-    private static final String EMPTY_ALPHA_SHORT = "";
     private static final int LAC = 65535;
     private static final int LATITUDE = 1292000;
     private static final int LONGITUDE = 1295000;
-    private static final int MCC = 120;
     private static final String MCC_STR = "120";
-    private static final int MNC = 260;
     private static final String MNC_STR = "260";
     private static final int NETWORK_ID = 65534;
     private static final int NRARFCN = 3279165;
     private static final int PCI = 503;
     private static final int PSC = 500;
-    private static final int RIL_TIMESTAMP_TYPE_OEM_RIL = 3;
     private static final int RSSNR = CellInfo.UNAVAILABLE;
     private static final int RSRP = -96;
     private static final int RSRQ = -10;
@@ -277,11 +271,9 @@
     private static final int TIMING_ADVANCE = 4;
     private static final long TIMESTAMP = 215924934;
     private static final int UARFCN = 690;
-    private static final int TYPE_CDMA = 2;
-    private static final int TYPE_GSM = 1;
-    private static final int TYPE_LTE = 3;
-    private static final int TYPE_WCDMA = 4;
-    private static final int TYPE_TD_SCDMA = 5;
+    private static final int CONNECTION_STATUS = CellInfo.CONNECTION_NONE;
+    private static final boolean ENDC_AVAILABLE = true;
+    private static final boolean REGISTERED = true;
 
     private static final int PROFILE_ID = 0;
     private static final String APN = "apn";
@@ -298,7 +290,6 @@
                     | TelephonyManager.NETWORK_TYPE_BITMASK_LTE);
     private static final int ROAMING_PROTOCOL = ApnSetting.PROTOCOL_IPV6;
     private static final int MTU = 1234;
-    private static final boolean PERSISTENT = true;
 
     private static final String[] ADDITIONAL_PLMNS = new String[] {"00101", "001001", "12345"};
 
@@ -341,31 +332,26 @@
                 RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE),
                 Phone.PREFERRED_CDMA_SUBSCRIPTION, 0, proxies);
         mRILUnderTest = spy(mRILInstance);
-        doReturn(mRadioProxy).when(mRILUnderTest).getRadioProxy(any());
-        doReturn(mDataProxy).when(mRILUnderTest).getRadioServiceProxy(eq(RadioDataProxy.class),
-                any());
+        doReturn(mRadioProxy).when(mRILUnderTest).getRadioProxy();
+        doReturn(mDataProxy).when(mRILUnderTest).getRadioServiceProxy(eq(RadioDataProxy.class));
         doReturn(mNetworkProxy).when(mRILUnderTest).getRadioServiceProxy(
-                eq(RadioNetworkProxy.class), any());
-        doReturn(mSimProxy).when(mRILUnderTest).getRadioServiceProxy(eq(RadioSimProxy.class),
-                any());
+                eq(RadioNetworkProxy.class));
+        doReturn(mSimProxy).when(mRILUnderTest).getRadioServiceProxy(eq(RadioSimProxy.class));
         doReturn(mRadioModemProxy).when(mRILUnderTest).getRadioServiceProxy(
-                eq(RadioModemProxy.class), any());
+                eq(RadioModemProxy.class));
         doReturn(false).when(mDataProxy).isEmpty();
         doReturn(false).when(mNetworkProxy).isEmpty();
         doReturn(false).when(mSimProxy).isEmpty();
         doReturn(false).when(mRadioModemProxy).isEmpty();
         try {
             for (int service = RIL.MIN_SERVICE_IDX; service <= RIL.MAX_SERVICE_IDX; service++) {
-                mHalVersionV10.put(service, new HalVersion(1, 0));
-                mHalVersionV11.put(service, new HalVersion(1, 1));
-                mHalVersionV12.put(service, new HalVersion(1, 2));
-                mHalVersionV13.put(service, new HalVersion(1, 3));
                 mHalVersionV14.put(service, new HalVersion(1, 4));
                 mHalVersionV15.put(service, new HalVersion(1, 5));
                 mHalVersionV16.put(service, new HalVersion(1, 6));
+                mHalVersionV20.put(service, new HalVersion(2, 0));
                 mHalVersionV21.put(service, new HalVersion(2, 1));
             }
-            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV10);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV14);
         } catch (Exception e) {
         }
     }
@@ -511,7 +497,6 @@
     @FlakyTest
     @Test
     public void testSupplySimDepersonalization() throws Exception {
-
         String controlKey = "1234";
         PersoSubState persoType = PersoSubState.PERSOSUBSTATE_SIM_NETWORK_PUK;
 
@@ -530,11 +515,7 @@
         mRILUnderTest.supplySimDepersonalization(persoType, controlKey, obtainMessage());
         verify(mRadioProxy).supplySimDepersonalization(
                 mSerialNumberCaptor.capture(),
-                eq((int) invokeMethod(
-                        mRILInstance,
-                        "convertPersoTypeToHalPersoType",
-                        new Class<?>[] {PersoSubState.class},
-                        new Object[] {persoType})),
+                RILUtils.convertToHalPersoType(persoType),
                 eq(controlKey));
         verifyRILResponse(
                 mRILUnderTest,
@@ -568,11 +549,7 @@
         mRILUnderTest.supplySimDepersonalization(persoType, controlKey, obtainMessage());
         verify(mRadioProxy).supplySimDepersonalization(
                 mSerialNumberCaptor.capture(),
-                eq((int) invokeMethod(
-                        mRILInstance,
-                        "convertPersoTypeToHalPersoType",
-                        new Class<?>[] {PersoSubState.class},
-                        new Object[] {persoType})),
+                RILUtils.convertToHalPersoType(persoType),
                 eq(controlKey));
         verifyRILResponse(
                 mRILUnderTest,
@@ -1166,18 +1143,11 @@
                 .setApnSetting(apnSetting)
                 .setPreferred(false)
                 .build();
-        boolean isRoaming = false;
 
-        mRILUnderTest.setInitialAttachApn(dataProfile, isRoaming, obtainMessage());
-        verify(mRadioProxy).setInitialAttachApn(
+        mRILUnderTest.setInitialAttachApn(dataProfile, obtainMessage());
+        verify(mRadioProxy).setInitialAttachApn_1_4(
                 mSerialNumberCaptor.capture(),
-                eq((DataProfileInfo) invokeMethod(
-                        mRILInstance,
-                        "convertToHalDataProfile10",
-                        new Class<?>[] {DataProfile.class},
-                        new Object[] {dataProfile})),
-                eq(dataProfile.isPersistent()),
-                eq(isRoaming));
+                eq(RILUtils.convertToHalDataProfile14(dataProfile)));
         verifyRILResponse(
                 mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SET_INITIAL_ATTACH_APN);
     }
@@ -1318,11 +1288,7 @@
         mRILUnderTest.nvResetConfig(resetType, obtainMessage());
         verify(mRadioProxy).nvResetConfig(
                 mSerialNumberCaptor.capture(),
-                eq((Integer) invokeMethod(
-                        mRILInstance,
-                        "convertToHalResetNvType",
-                        new Class<?>[] {Integer.TYPE},
-                        new Object[] {resetType})));
+                RILUtils.convertToHalResetNvType(resetType));
         verifyRILResponse(
                 mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_NV_RESET_CONFIG);
     }
@@ -1377,33 +1343,6 @@
 
     @FlakyTest
     @Test
-    public void testStartLceService() throws Exception {
-        int reportIntervalMs = 1000;
-        boolean pullMode = false;
-        mRILUnderTest.startLceService(reportIntervalMs, pullMode, obtainMessage());
-        verify(mRadioProxy).startLceService(
-                mSerialNumberCaptor.capture(), eq(reportIntervalMs), eq(pullMode));
-        verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_START_LCE);
-    }
-
-    @FlakyTest
-    @Test
-    public void testStopLceService() throws Exception {
-        mRILUnderTest.stopLceService(obtainMessage());
-        verify(mRadioProxy).stopLceService(mSerialNumberCaptor.capture());
-        verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_STOP_LCE);
-    }
-
-    @FlakyTest
-    @Test
-    public void testPullLceData() throws Exception {
-        mRILUnderTest.pullLceData(obtainMessage());
-        verify(mRadioProxy).pullLceData(mSerialNumberCaptor.capture());
-        verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_PULL_LCEDATA);
-    }
-
-    @FlakyTest
-    @Test
     public void testGetModemActivityInfo() throws Exception {
         mRILUnderTest.getModemActivityInfo(obtainMessage(), new WorkSource());
         verify(mRadioProxy).getModemActivityInfo(mSerialNumberCaptor.capture());
@@ -1606,460 +1545,694 @@
         return respInfo;
     }
 
+    private android.hardware.radio.V1_2.CellIdentityOperatorNames getCellIdentityOperatorNames() {
+        android.hardware.radio.V1_2.CellIdentityOperatorNames operatorNames =
+                new android.hardware.radio.V1_2.CellIdentityOperatorNames();
+        operatorNames.alphaLong = ALPHA_LONG;
+        operatorNames.alphaShort = ALPHA_SHORT;
+
+        return operatorNames;
+    }
+
+    private android.hardware.radio.V1_2.CellIdentityLte getCellIdentityLte_1_2() {
+        android.hardware.radio.V1_0.CellIdentityLte cellIdentity0 =
+                new android.hardware.radio.V1_0.CellIdentityLte();
+        cellIdentity0.mcc = MCC_STR;
+        cellIdentity0.mnc = MNC_STR;
+        cellIdentity0.ci = CI;
+        cellIdentity0.pci = PCI;
+        cellIdentity0.tac = TAC;
+        cellIdentity0.earfcn = EARFCN;
+
+        android.hardware.radio.V1_2.CellIdentityLte cellIdentity =
+                new android.hardware.radio.V1_2.CellIdentityLte();
+        cellIdentity.base = cellIdentity0;
+        cellIdentity.operatorNames = getCellIdentityOperatorNames();
+        cellIdentity.bandwidth = BANDWIDTH;
+
+        return cellIdentity;
+    }
+
+    private android.hardware.radio.V1_0.LteSignalStrength getLteSignalStrength_1_0() {
+        android.hardware.radio.V1_0.LteSignalStrength signalStrength =
+                new android.hardware.radio.V1_0.LteSignalStrength();
+        signalStrength.signalStrength = RSSI_ASU;
+        signalStrength.rsrp = -RSRP;
+        signalStrength.rsrq = -RSRQ;
+        signalStrength.rssnr = RSSNR;
+        signalStrength.cqi = CQI;
+        signalStrength.timingAdvance = TIMING_ADVANCE;
+
+        return signalStrength;
+    }
+
+    private android.hardware.radio.V1_4.CellInfo getCellInfo_1_4ForLte() {
+        android.hardware.radio.V1_2.CellInfoLte cellInfo2 =
+                new android.hardware.radio.V1_2.CellInfoLte();
+        cellInfo2.cellIdentityLte = getCellIdentityLte_1_2();
+        cellInfo2.signalStrengthLte = getLteSignalStrength_1_0();
+
+        android.hardware.radio.V1_4.CellConfigLte cellConfig =
+                new android.hardware.radio.V1_4.CellConfigLte();
+        cellConfig.isEndcAvailable = ENDC_AVAILABLE;
+
+        android.hardware.radio.V1_4.CellInfoLte cellInfoLte =
+                new android.hardware.radio.V1_4.CellInfoLte();
+        cellInfoLte.base = cellInfo2;
+        cellInfoLte.cellConfig = cellConfig;
+
+        android.hardware.radio.V1_4.CellInfo cellInfo = new android.hardware.radio.V1_4.CellInfo();
+        cellInfo.isRegistered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.info.lte(cellInfoLte);
+
+        return cellInfo;
+    }
+
     @Test
-    public void testConvertHalCellInfoListForLTE() {
-        android.hardware.radio.V1_0.CellInfoLte lte = new android.hardware.radio.V1_0.CellInfoLte();
-        lte.cellIdentityLte.ci = CI;
-        lte.cellIdentityLte.pci = PCI;
-        lte.cellIdentityLte.tac = TAC;
-        lte.cellIdentityLte.earfcn = EARFCN;
-        lte.cellIdentityLte.mcc = MCC_STR;
-        lte.cellIdentityLte.mnc = MNC_STR;
-        lte.signalStrengthLte.signalStrength = RSSI_ASU;
-        lte.signalStrengthLte.rsrp = -RSRP;
-        lte.signalStrengthLte.rsrq = -RSRQ;
-        lte.signalStrengthLte.rssnr = RSSNR;
-        lte.signalStrengthLte.cqi = CQI;
-        lte.signalStrengthLte.timingAdvance = TIMING_ADVANCE;
-        android.hardware.radio.V1_0.CellInfo record = new android.hardware.radio.V1_0.CellInfo();
-        record.cellInfoType = TYPE_LTE;
-        record.registered = false;
-        record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
-        record.timeStamp = TIMESTAMP;
-        record.lte.add(lte);
+    public void testConvertHalCellInfoList_1_4ForLte() {
         ArrayList<Object> records = new ArrayList<>();
-        records.add(record);
+        records.add(getCellInfo_1_4ForLte());
 
         ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
 
         assertEquals(1, ret.size());
         CellInfoLte cellInfoLte = (CellInfoLte) ret.get(0);
-        CellInfoLte expected = new CellInfoLte();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityLte cil = new CellIdentityLte(CI, PCI, TAC, EARFCN, new int[] {},
-                Integer.MAX_VALUE, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT,
-                Collections.emptyList(), null);
-        CellSignalStrengthLte css = new CellSignalStrengthLte(
-                RSSI, RSRP, RSRQ, RSSNR, CQI, TIMING_ADVANCE);
-        expected.setCellIdentity(cil);
-        expected.setCellSignalStrength(css);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
         cellInfoLte.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        CellIdentityLte cellIdentityLte = new CellIdentityLte(CI, PCI, TAC, EARFCN, new int[] {},
+                BANDWIDTH, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT, Collections.emptyList(),
+                null);
+        CellSignalStrengthLte cellSignalStrengthLte = new CellSignalStrengthLte(
+                RSSI, RSRP, RSRQ, RSSNR, CQI, TIMING_ADVANCE);
+        CellConfigLte cellConfigLte = new CellConfigLte(ENDC_AVAILABLE);
+        CellInfoLte expected = new CellInfoLte(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityLte, cellSignalStrengthLte, cellConfigLte);
         assertEquals(expected, cellInfoLte);
     }
 
+    private android.hardware.radio.V1_5.OptionalCsgInfo getOptionalCsgInfo() {
+        android.hardware.radio.V1_5.ClosedSubscriberGroupInfo closedSubscriberGroupInfo =
+                new android.hardware.radio.V1_5.ClosedSubscriberGroupInfo();
+        closedSubscriberGroupInfo.csgIndication = CSG_INDICATION;
+        closedSubscriberGroupInfo.homeNodebName = HOME_NODEB_NAME;
+        closedSubscriberGroupInfo.csgIdentity = CSG_IDENTITY;
+
+        android.hardware.radio.V1_5.OptionalCsgInfo optionalCsgInfo =
+                new android.hardware.radio.V1_5.OptionalCsgInfo();
+        optionalCsgInfo.csgInfo(closedSubscriberGroupInfo);
+
+        return optionalCsgInfo;
+    }
+
+    private android.hardware.radio.V1_5.CellIdentityLte getCellIdentityLte_1_5() {
+        android.hardware.radio.V1_5.CellIdentityLte cellIdentity =
+                new android.hardware.radio.V1_5.CellIdentityLte();
+        cellIdentity.base = getCellIdentityLte_1_2();
+        cellIdentity.additionalPlmns = new ArrayList<>(Arrays.asList(ADDITIONAL_PLMNS));
+        cellIdentity.optionalCsgInfo = getOptionalCsgInfo();
+        cellIdentity.bands = BANDS;
+
+        return cellIdentity;
+    }
+
     @Test
-    public void testConvertHalCellInfoListForGSM() {
-        android.hardware.radio.V1_0.CellInfoGsm cellinfo =
-                new android.hardware.radio.V1_0.CellInfoGsm();
-        cellinfo.cellIdentityGsm.lac = LAC;
-        cellinfo.cellIdentityGsm.cid = CID;
-        cellinfo.cellIdentityGsm.bsic = BSIC;
-        cellinfo.cellIdentityGsm.arfcn = ARFCN;
-        cellinfo.cellIdentityGsm.mcc = MCC_STR;
-        cellinfo.cellIdentityGsm.mnc = MNC_STR;
-        cellinfo.signalStrengthGsm.signalStrength = RSSI_ASU;
-        cellinfo.signalStrengthGsm.bitErrorRate = BIT_ERROR_RATE;
-        cellinfo.signalStrengthGsm.timingAdvance = TIMING_ADVANCE;
-        android.hardware.radio.V1_0.CellInfo record = new android.hardware.radio.V1_0.CellInfo();
-        record.cellInfoType = TYPE_GSM;
-        record.registered = false;
-        record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
-        record.timeStamp = TIMESTAMP;
-        record.gsm.add(cellinfo);
+    public void testConvertHalCellInfoList_1_5ForLte() {
+        android.hardware.radio.V1_5.CellInfoLte cellInfoLte =
+                new android.hardware.radio.V1_5.CellInfoLte();
+        cellInfoLte.cellIdentityLte = getCellIdentityLte_1_5();
+        cellInfoLte.signalStrengthLte = getLteSignalStrength_1_0();
+
+        android.hardware.radio.V1_5.CellInfo cellInfo = new android.hardware.radio.V1_5.CellInfo();
+        cellInfo.registered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.ratSpecificInfo.lte(cellInfoLte);
+
         ArrayList<Object> records = new ArrayList<>();
-        records.add(record);
+        records.add(cellInfo);
+
+        ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
+
+        assertEquals(1, ret.size());
+        CellInfoLte cil = (CellInfoLte) ret.get(0);
+        cil.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        Set<String> additionalPlmns = new HashSet<>();
+        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
+        ClosedSubscriberGroupInfo closedSubscriberGroupInfo =
+                new ClosedSubscriberGroupInfo(CSG_INDICATION, HOME_NODEB_NAME, CSG_IDENTITY);
+        CellIdentityLte cellIdentityLte = new CellIdentityLte(CI, PCI, TAC, EARFCN,
+                BANDS.stream().mapToInt(i -> i).toArray(), BANDWIDTH, MCC_STR, MNC_STR, ALPHA_LONG,
+                ALPHA_SHORT, additionalPlmns, closedSubscriberGroupInfo);
+        CellSignalStrengthLte cellSignalStrengthLte = new CellSignalStrengthLte(
+                RSSI, RSRP, RSRQ, RSSNR, CQI, TIMING_ADVANCE);
+        CellInfoLte expected = new CellInfoLte(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityLte, cellSignalStrengthLte, new CellConfigLte());
+        assertEquals(expected, cil);
+    }
+
+    @Test
+    public void testConvertHalCellInfoList_1_6ForLte() {
+        android.hardware.radio.V1_6.LteSignalStrength signalStrength =
+                new android.hardware.radio.V1_6.LteSignalStrength();
+        signalStrength.base = getLteSignalStrength_1_0();
+        signalStrength.cqiTableIndex = CQI_TABLE_INDEX;
+
+        android.hardware.radio.V1_6.CellInfoLte cellInfoLte =
+                new android.hardware.radio.V1_6.CellInfoLte();
+        cellInfoLte.cellIdentityLte = getCellIdentityLte_1_5();
+        cellInfoLte.signalStrengthLte = signalStrength;
+
+        android.hardware.radio.V1_6.CellInfo cellInfo = new android.hardware.radio.V1_6.CellInfo();
+        cellInfo.registered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.ratSpecificInfo.lte(cellInfoLte);
+
+        ArrayList<Object> records = new ArrayList<>();
+        records.add(cellInfo);
+
+        ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
+
+        assertEquals(1, ret.size());
+        CellInfoLte cil = (CellInfoLte) ret.get(0);
+        cil.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        Set<String> additionalPlmns = new HashSet<>();
+        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
+        ClosedSubscriberGroupInfo closedSubscriberGroupInfo =
+                new ClosedSubscriberGroupInfo(CSG_INDICATION, HOME_NODEB_NAME, CSG_IDENTITY);
+        CellIdentityLte cellIdentityLte = new CellIdentityLte(CI, PCI, TAC, EARFCN,
+                BANDS.stream().mapToInt(i -> i).toArray(), BANDWIDTH, MCC_STR, MNC_STR, ALPHA_LONG,
+                ALPHA_SHORT, additionalPlmns, closedSubscriberGroupInfo);
+        CellSignalStrengthLte cellSignalStrengthLte = new CellSignalStrengthLte(
+                RSSI, RSRP, RSRQ, RSSNR, CQI_TABLE_INDEX, CQI, TIMING_ADVANCE);
+        CellInfoLte expected = new CellInfoLte(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityLte, cellSignalStrengthLte, new CellConfigLte());
+        assertEquals(expected, cil);
+    }
+
+    private android.hardware.radio.V1_2.CellIdentityGsm getCellIdentityGsm_1_2() {
+        android.hardware.radio.V1_0.CellIdentityGsm cellIdentity0 =
+                new android.hardware.radio.V1_0.CellIdentityGsm();
+        cellIdentity0.mcc = MCC_STR;
+        cellIdentity0.mnc = MNC_STR;
+        cellIdentity0.lac = LAC;
+        cellIdentity0.cid = CID;
+        cellIdentity0.arfcn = ARFCN;
+        cellIdentity0.bsic = BSIC;
+
+        android.hardware.radio.V1_2.CellIdentityGsm cellIdentity =
+                new android.hardware.radio.V1_2.CellIdentityGsm();
+        cellIdentity.base = cellIdentity0;
+        cellIdentity.operatorNames = getCellIdentityOperatorNames();
+
+        return cellIdentity;
+    }
+
+    private android.hardware.radio.V1_0.GsmSignalStrength getGsmSignalStrength_1_0() {
+        android.hardware.radio.V1_0.GsmSignalStrength signalStrength =
+                new android.hardware.radio.V1_0.GsmSignalStrength();
+        signalStrength.signalStrength = RSSI_ASU;
+        signalStrength.bitErrorRate = BIT_ERROR_RATE;
+        signalStrength.timingAdvance = TIMING_ADVANCE;
+
+        return signalStrength;
+    }
+
+    @Test
+    public void testConvertHalCellInfoList_1_4ForGsm() {
+        android.hardware.radio.V1_2.CellInfoGsm cellInfoGsm =
+                new android.hardware.radio.V1_2.CellInfoGsm();
+        cellInfoGsm.cellIdentityGsm = getCellIdentityGsm_1_2();
+        cellInfoGsm.signalStrengthGsm = getGsmSignalStrength_1_0();
+
+        android.hardware.radio.V1_4.CellInfo cellInfo = new android.hardware.radio.V1_4.CellInfo();
+        cellInfo.isRegistered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.info.gsm(cellInfoGsm);
+
+        ArrayList<Object> records = new ArrayList<>();
+        records.add(cellInfo);
+
+        ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
+
+        assertEquals(1, ret.size());
+        CellInfoGsm cig = (CellInfoGsm) ret.get(0);
+        cig.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        CellIdentityGsm cellIdentityGsm = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, MCC_STR,
+                MNC_STR, ALPHA_LONG, ALPHA_SHORT, Collections.emptyList());
+        CellSignalStrengthGsm cellSignalStrengthGsm = new CellSignalStrengthGsm(
+                RSSI, BIT_ERROR_RATE, TIMING_ADVANCE);
+        CellInfoGsm expected = new CellInfoGsm(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityGsm, cellSignalStrengthGsm);
+        assertEquals(expected, cig);
+    }
+
+    private android.hardware.radio.V1_5.CellInfoGsm getCellInfoGsm_1_5() {
+        android.hardware.radio.V1_5.CellIdentityGsm cellIdentity =
+                new android.hardware.radio.V1_5.CellIdentityGsm();
+        cellIdentity.base = getCellIdentityGsm_1_2();
+        cellIdentity.additionalPlmns = new ArrayList<>(Arrays.asList(ADDITIONAL_PLMNS));
+
+        android.hardware.radio.V1_5.CellInfoGsm cellInfo =
+                new android.hardware.radio.V1_5.CellInfoGsm();
+        cellInfo.cellIdentityGsm = cellIdentity;
+        cellInfo.signalStrengthGsm = getGsmSignalStrength_1_0();
+
+        return cellInfo;
+    }
+
+    @Test
+    public void testConvertHalCellInfoList_1_5ForGsm() {
+        android.hardware.radio.V1_5.CellInfo cellInfo = new android.hardware.radio.V1_5.CellInfo();
+        cellInfo.registered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.ratSpecificInfo.gsm(getCellInfoGsm_1_5());
+
+        ArrayList<Object> records = new ArrayList<>();
+        records.add(cellInfo);
 
         ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
 
         assertEquals(1, ret.size());
         CellInfoGsm cellInfoGsm = (CellInfoGsm) ret.get(0);
-        CellInfoGsm expected = new CellInfoGsm();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityGsm ci = new CellIdentityGsm(
-                LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT,
-                Collections.emptyList());
-        CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
-                RSSI, BIT_ERROR_RATE, TIMING_ADVANCE);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
         cellInfoGsm.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        Set<String> additionalPlmns = new HashSet<>();
+        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
+        CellIdentityGsm cellIdentityGsm = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, MCC_STR,
+                MNC_STR, ALPHA_LONG, ALPHA_SHORT, additionalPlmns);
+        CellSignalStrengthGsm cellSignalStrengthGsm = new CellSignalStrengthGsm(
+                RSSI, BIT_ERROR_RATE, TIMING_ADVANCE);
+        CellInfoGsm expected = new CellInfoGsm(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityGsm, cellSignalStrengthGsm);
         assertEquals(expected, cellInfoGsm);
     }
 
     @Test
-    public void testConvertHalCellInfoListForWcdma() {
-        android.hardware.radio.V1_0.CellInfoWcdma cellinfo =
-                new android.hardware.radio.V1_0.CellInfoWcdma();
-        cellinfo.cellIdentityWcdma.lac = LAC;
-        cellinfo.cellIdentityWcdma.cid = CID;
-        cellinfo.cellIdentityWcdma.psc = PSC;
-        cellinfo.cellIdentityWcdma.uarfcn = UARFCN;
-        cellinfo.cellIdentityWcdma.mcc = MCC_STR;
-        cellinfo.cellIdentityWcdma.mnc = MNC_STR;
-        cellinfo.signalStrengthWcdma.signalStrength = RSSI_ASU;
-        cellinfo.signalStrengthWcdma.bitErrorRate = BIT_ERROR_RATE;
-        android.hardware.radio.V1_0.CellInfo record = new android.hardware.radio.V1_0.CellInfo();
-        record.cellInfoType = TYPE_WCDMA;
-        record.registered = false;
-        record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
-        record.timeStamp = TIMESTAMP;
-        record.wcdma.add(cellinfo);
+    public void testConvertHalCellInfoList_1_6ForGsm() {
+        android.hardware.radio.V1_6.CellInfo cellInfo = new android.hardware.radio.V1_6.CellInfo();
+        cellInfo.registered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.ratSpecificInfo.gsm(getCellInfoGsm_1_5());
+
         ArrayList<Object> records = new ArrayList<>();
-        records.add(record);
+        records.add(cellInfo);
+
+        ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
+
+        assertEquals(1, ret.size());
+        CellInfoGsm cellInfoGsm = (CellInfoGsm) ret.get(0);
+        cellInfoGsm.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        Set<String> additionalPlmns = new HashSet<>();
+        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
+        CellIdentityGsm cellIdentityGsm = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, MCC_STR,
+                MNC_STR, ALPHA_LONG, ALPHA_SHORT, additionalPlmns);
+        CellSignalStrengthGsm cellSignalStrengthGsm = new CellSignalStrengthGsm(
+                RSSI, BIT_ERROR_RATE, TIMING_ADVANCE);
+        CellInfoGsm expected = new CellInfoGsm(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityGsm, cellSignalStrengthGsm);
+        assertEquals(expected, cellInfoGsm);
+    }
+
+    private android.hardware.radio.V1_2.CellIdentityWcdma getCellIdentityWcdma_1_2() {
+        android.hardware.radio.V1_0.CellIdentityWcdma cellIdentity0 =
+                new android.hardware.radio.V1_0.CellIdentityWcdma();
+        cellIdentity0.mcc = MCC_STR;
+        cellIdentity0.mnc = MNC_STR;
+        cellIdentity0.lac = LAC;
+        cellIdentity0.cid = CID;
+        cellIdentity0.psc = PSC;
+        cellIdentity0.uarfcn = UARFCN;
+
+        android.hardware.radio.V1_2.CellIdentityWcdma cellIdentity =
+                new android.hardware.radio.V1_2.CellIdentityWcdma();
+        cellIdentity.base = cellIdentity0;
+        cellIdentity.operatorNames = getCellIdentityOperatorNames();
+
+        return cellIdentity;
+    }
+
+    private android.hardware.radio.V1_2.WcdmaSignalStrength getWcdmaSignalStrength_1_2() {
+        android.hardware.radio.V1_0.WcdmaSignalStrength signalStrength0 =
+                new android.hardware.radio.V1_0.WcdmaSignalStrength();
+        signalStrength0.signalStrength = RSSI_ASU;
+        signalStrength0.bitErrorRate = BIT_ERROR_RATE;
+
+        android.hardware.radio.V1_2.WcdmaSignalStrength signalStrength =
+                new android.hardware.radio.V1_2.WcdmaSignalStrength();
+        signalStrength.base = signalStrength0;
+        signalStrength.rscp = RSCP_ASU;
+        signalStrength.ecno = ECNO_ASU;
+
+        return signalStrength;
+    }
+
+    @Test
+    public void testConvertHalCellInfoList_1_4ForWcdma() {
+        android.hardware.radio.V1_2.CellInfoWcdma cellInfoWcdma =
+                new android.hardware.radio.V1_2.CellInfoWcdma();
+        cellInfoWcdma.cellIdentityWcdma = getCellIdentityWcdma_1_2();
+        cellInfoWcdma.signalStrengthWcdma = getWcdmaSignalStrength_1_2();
+
+        android.hardware.radio.V1_4.CellInfo cellInfo = new android.hardware.radio.V1_4.CellInfo();
+        cellInfo.isRegistered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.info.wcdma(cellInfoWcdma);
+
+        ArrayList<Object> records = new ArrayList<>();
+        records.add(cellInfo);
+
+        ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
+
+        assertEquals(1, ret.size());
+        CellInfoWcdma ciw = (CellInfoWcdma) ret.get(0);
+        ciw.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        CellIdentityWcdma cellIdentityWcdma = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, MCC_STR,
+                MNC_STR, ALPHA_LONG, ALPHA_SHORT, Collections.emptyList(), null);
+        CellSignalStrengthWcdma cellSignalStrengthWcdma = new CellSignalStrengthWcdma(
+                RSSI, BIT_ERROR_RATE, RSCP, ECNO);
+        CellInfoWcdma expected = new CellInfoWcdma(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityWcdma, cellSignalStrengthWcdma);
+        assertEquals(expected, ciw);
+    }
+
+    private android.hardware.radio.V1_5.CellInfoWcdma getCellInfoWcdma_1_5() {
+        android.hardware.radio.V1_5.CellIdentityWcdma cellIdentity =
+                new android.hardware.radio.V1_5.CellIdentityWcdma();
+        cellIdentity.base = getCellIdentityWcdma_1_2();
+        cellIdentity.additionalPlmns = new ArrayList<>(Arrays.asList(ADDITIONAL_PLMNS));
+        cellIdentity.optionalCsgInfo = getOptionalCsgInfo();
+
+        android.hardware.radio.V1_5.CellInfoWcdma cellInfo =
+                new android.hardware.radio.V1_5.CellInfoWcdma();
+        cellInfo.cellIdentityWcdma = cellIdentity;
+        cellInfo.signalStrengthWcdma = getWcdmaSignalStrength_1_2();
+
+        return cellInfo;
+    }
+
+    @Test
+    public void testConvertHalCellInfoList_1_5ForWcdma() {
+        android.hardware.radio.V1_5.CellInfo cellInfo = new android.hardware.radio.V1_5.CellInfo();
+        cellInfo.registered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.ratSpecificInfo.wcdma(getCellInfoWcdma_1_5());
+
+        ArrayList<Object> records = new ArrayList<>();
+        records.add(cellInfo);
 
         ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
 
         assertEquals(1, ret.size());
         CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ret.get(0);
-        CellInfoWcdma expected = new CellInfoWcdma();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityWcdma ci = new CellIdentityWcdma(
-                LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT,
-                Collections.emptyList(), null);
-        CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
-                RSSI, BIT_ERROR_RATE, Integer.MAX_VALUE, Integer.MAX_VALUE);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
         cellInfoWcdma.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        Set<String> additionalPlmns = new HashSet<>();
+        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
+        ClosedSubscriberGroupInfo closedSubscriberGroupInfo =
+                new ClosedSubscriberGroupInfo(CSG_INDICATION, HOME_NODEB_NAME, CSG_IDENTITY);
+        CellIdentityWcdma cellIdentityWcdma = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, MCC_STR,
+                MNC_STR, ALPHA_LONG, ALPHA_SHORT, additionalPlmns, closedSubscriberGroupInfo);
+        CellSignalStrengthWcdma cellSignalStrengthWcdma = new CellSignalStrengthWcdma(
+                RSSI, BIT_ERROR_RATE, RSCP, ECNO);
+        CellInfoWcdma expected = new CellInfoWcdma(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityWcdma, cellSignalStrengthWcdma);
         assertEquals(expected, cellInfoWcdma);
     }
 
-    private static void initializeCellIdentityTdscdma_1_2(
-            android.hardware.radio.V1_2.CellIdentityTdscdma cid) {
-        cid.base.lac = LAC;
-        cid.base.cid = CID;
-        cid.base.cpid = PSC;
-        cid.base.mcc = MCC_STR;
-        cid.base.mnc = MNC_STR;
-        cid.uarfcn = UARFCN;
-        cid.operatorNames.alphaLong = ALPHA_LONG;
-        cid.operatorNames.alphaShort = ALPHA_SHORT;
+    @Test
+    public void testConvertHalCellInfoList_1_6ForWcdma() {
+        android.hardware.radio.V1_6.CellInfo cellInfo = new android.hardware.radio.V1_6.CellInfo();
+        cellInfo.registered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.ratSpecificInfo.wcdma(getCellInfoWcdma_1_5());
+
+        ArrayList<Object> records = new ArrayList<>();
+        records.add(cellInfo);
+
+        ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
+
+        assertEquals(1, ret.size());
+        CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ret.get(0);
+        cellInfoWcdma.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        Set<String> additionalPlmns = new HashSet<>();
+        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
+        ClosedSubscriberGroupInfo closedSubscriberGroupInfo =
+                new ClosedSubscriberGroupInfo(CSG_INDICATION, HOME_NODEB_NAME, CSG_IDENTITY);
+        CellIdentityWcdma cellIdentityWcdma = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, MCC_STR,
+                MNC_STR, ALPHA_LONG, ALPHA_SHORT, additionalPlmns, closedSubscriberGroupInfo);
+        CellSignalStrengthWcdma cellSignalStrengthWcdma = new CellSignalStrengthWcdma(
+                RSSI, BIT_ERROR_RATE, RSCP, ECNO);
+        CellInfoWcdma expected = new CellInfoWcdma(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityWcdma, cellSignalStrengthWcdma);
+        assertEquals(expected, cellInfoWcdma);
+    }
+
+    private android.hardware.radio.V1_2.CellIdentityTdscdma getCellIdentityTdscdma_1_2() {
+        android.hardware.radio.V1_0.CellIdentityTdscdma cellIdentity0 =
+                new android.hardware.radio.V1_0.CellIdentityTdscdma();
+        cellIdentity0.mcc = MCC_STR;
+        cellIdentity0.mnc = MNC_STR;
+        cellIdentity0.lac = LAC;
+        cellIdentity0.cid = CID;
+        cellIdentity0.cpid = PSC;
+
+        android.hardware.radio.V1_2.CellIdentityTdscdma cellIdentity =
+                new android.hardware.radio.V1_2.CellIdentityTdscdma();
+        cellIdentity.base = cellIdentity0;
+        cellIdentity.uarfcn = UARFCN;
+        cellIdentity.operatorNames = getCellIdentityOperatorNames();
+
+        return cellIdentity;
+    }
+
+    private android.hardware.radio.V1_2.TdscdmaSignalStrength getTdscdmaSignalStrength_1_2() {
+        android.hardware.radio.V1_2.TdscdmaSignalStrength signalStrength =
+                new android.hardware.radio.V1_2.TdscdmaSignalStrength();
+        signalStrength.signalStrength = RSSI_ASU;
+        signalStrength.bitErrorRate = BIT_ERROR_RATE;
+        signalStrength.rscp = RSCP_ASU;
+
+        return signalStrength;
     }
 
     @Test
-    public void testConvertHalCellInfoListForTdscdma() {
-        android.hardware.radio.V1_2.CellInfoTdscdma cellinfo =
+    public void testConvertHalCellInfoList_1_4ForTdscdma() {
+        android.hardware.radio.V1_2.CellInfoTdscdma cellInfoTdscdma =
                 new android.hardware.radio.V1_2.CellInfoTdscdma();
-        initializeCellIdentityTdscdma_1_2(cellinfo.cellIdentityTdscdma);
+        cellInfoTdscdma.cellIdentityTdscdma = getCellIdentityTdscdma_1_2();
+        cellInfoTdscdma.signalStrengthTdscdma = getTdscdmaSignalStrength_1_2();
 
-        cellinfo.signalStrengthTdscdma.signalStrength = RSSI_ASU;
-        cellinfo.signalStrengthTdscdma.bitErrorRate = BIT_ERROR_RATE;
-        cellinfo.signalStrengthTdscdma.rscp = RSCP_ASU;
-        android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
-        record.cellInfoType = TYPE_TD_SCDMA;
-        record.registered = false;
-        record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
-        record.timeStamp = TIMESTAMP;
-        record.tdscdma.add(cellinfo);
+        android.hardware.radio.V1_4.CellInfo cellInfo = new android.hardware.radio.V1_4.CellInfo();
+        cellInfo.isRegistered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.info.tdscdma(cellInfoTdscdma);
+
         ArrayList<Object> records = new ArrayList<>();
-        records.add(record);
+        records.add(cellInfo);
+
+        ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
+
+        assertEquals(1, ret.size());
+        CellInfoTdscdma cit = (CellInfoTdscdma) ret.get(0);
+        cit.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        CellIdentityTdscdma cellIdentityTdscdma = new CellIdentityTdscdma(
+                MCC_STR, MNC_STR, LAC, CID, PSC, UARFCN, ALPHA_LONG, ALPHA_SHORT,
+                Collections.emptyList(), null);
+        CellSignalStrengthTdscdma cellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(
+                RSSI, BIT_ERROR_RATE, RSCP);
+        CellInfoTdscdma expected = new CellInfoTdscdma(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityTdscdma, cellSignalStrengthTdscdma);
+        assertEquals(expected, cit);
+    }
+
+    private android.hardware.radio.V1_5.CellInfoTdscdma getCellInfoTdscdma_1_5() {
+        android.hardware.radio.V1_5.CellIdentityTdscdma cellIdentity =
+                new android.hardware.radio.V1_5.CellIdentityTdscdma();
+        cellIdentity.base = getCellIdentityTdscdma_1_2();
+        cellIdentity.additionalPlmns = new ArrayList<>(Arrays.asList(ADDITIONAL_PLMNS));
+        cellIdentity.optionalCsgInfo = getOptionalCsgInfo();
+
+        android.hardware.radio.V1_5.CellInfoTdscdma cellInfo =
+                new android.hardware.radio.V1_5.CellInfoTdscdma();
+        cellInfo.cellIdentityTdscdma = cellIdentity;
+        cellInfo.signalStrengthTdscdma = getTdscdmaSignalStrength_1_2();
+
+        return cellInfo;
+    }
+
+    @Test
+    public void testConvertHalCellInfoList_1_5ForTdscdma() {
+        android.hardware.radio.V1_5.CellInfo cellInfo = new android.hardware.radio.V1_5.CellInfo();
+        cellInfo.registered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.ratSpecificInfo.tdscdma(getCellInfoTdscdma_1_5());
+
+        ArrayList<Object> records = new ArrayList<>();
+        records.add(cellInfo);
 
         ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
 
         assertEquals(1, ret.size());
         CellInfoTdscdma cellInfoTdscdma = (CellInfoTdscdma) ret.get(0);
-        CellInfoTdscdma expected = new CellInfoTdscdma();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
-        CellIdentityTdscdma ci = new CellIdentityTdscdma(
-                MCC_STR, MNC_STR, LAC, CID, PSC, UARFCN, ALPHA_LONG, ALPHA_SHORT,
-                Collections.emptyList(), null);
-        CellSignalStrengthTdscdma cs = new CellSignalStrengthTdscdma(
-                RSSI, BIT_ERROR_RATE, RSCP);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
         cellInfoTdscdma.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        Set<String> additionalPlmns = new HashSet<>();
+        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
+        ClosedSubscriberGroupInfo closedSubscriberGroupInfo =
+                new ClosedSubscriberGroupInfo(CSG_INDICATION, HOME_NODEB_NAME, CSG_IDENTITY);
+        CellIdentityTdscdma cellIdentityTdscdma = new CellIdentityTdscdma(
+                MCC_STR, MNC_STR, LAC, CID, PSC, UARFCN, ALPHA_LONG, ALPHA_SHORT,
+                additionalPlmns, closedSubscriberGroupInfo);
+        CellSignalStrengthTdscdma cellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(
+                RSSI, BIT_ERROR_RATE, RSCP);
+        CellInfoTdscdma expected = new CellInfoTdscdma(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityTdscdma, cellSignalStrengthTdscdma);
         assertEquals(expected, cellInfoTdscdma);
     }
 
     @Test
-    public void testConvertHalCellInfoListForCdma() {
-        android.hardware.radio.V1_0.CellInfoCdma cellinfo =
-                new android.hardware.radio.V1_0.CellInfoCdma();
-        cellinfo.cellIdentityCdma.networkId = NETWORK_ID;
-        cellinfo.cellIdentityCdma.systemId = SYSTEM_ID;
-        cellinfo.cellIdentityCdma.baseStationId = BASESTATION_ID;
-        cellinfo.cellIdentityCdma.longitude = LONGITUDE;
-        cellinfo.cellIdentityCdma.latitude = LATITUDE;
-        cellinfo.signalStrengthCdma.dbm = -DBM;
-        cellinfo.signalStrengthCdma.ecio = -ECIO;
-        cellinfo.signalStrengthEvdo.dbm = -DBM;
-        cellinfo.signalStrengthEvdo.ecio = -ECIO;
-        cellinfo.signalStrengthEvdo.signalNoiseRatio = SIGNAL_NOISE_RATIO;
-        android.hardware.radio.V1_0.CellInfo record = new android.hardware.radio.V1_0.CellInfo();
-        record.cellInfoType = TYPE_CDMA;
-        record.registered = false;
-        record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
-        record.timeStamp = TIMESTAMP;
-        record.cdma.add(cellinfo);
+    public void testConvertHalCellInfoList_1_6ForTdscdma() {
+        android.hardware.radio.V1_6.CellInfo cellInfo = new android.hardware.radio.V1_6.CellInfo();
+        cellInfo.registered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.ratSpecificInfo.tdscdma(getCellInfoTdscdma_1_5());
+
         ArrayList<Object> records = new ArrayList<>();
-        records.add(record);
+        records.add(cellInfo);
+
+        ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
+
+        assertEquals(1, ret.size());
+        CellInfoTdscdma cellInfoTdscdma = (CellInfoTdscdma) ret.get(0);
+        cellInfoTdscdma.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        Set<String> additionalPlmns = new HashSet<>();
+        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
+        ClosedSubscriberGroupInfo closedSubscriberGroupInfo =
+                new ClosedSubscriberGroupInfo(CSG_INDICATION, HOME_NODEB_NAME, CSG_IDENTITY);
+        CellIdentityTdscdma cellIdentityTdscdma = new CellIdentityTdscdma(
+                MCC_STR, MNC_STR, LAC, CID, PSC, UARFCN, ALPHA_LONG, ALPHA_SHORT,
+                additionalPlmns, closedSubscriberGroupInfo);
+        CellSignalStrengthTdscdma cellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(
+                RSSI, BIT_ERROR_RATE, RSCP);
+        CellInfoTdscdma expected = new CellInfoTdscdma(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityTdscdma, cellSignalStrengthTdscdma);
+        assertEquals(expected, cellInfoTdscdma);
+    }
+
+    private android.hardware.radio.V1_2.CellInfoCdma getCellInfoCdma_1_2() {
+        android.hardware.radio.V1_0.CellIdentityCdma cellIdentity0 =
+                new android.hardware.radio.V1_0.CellIdentityCdma();
+        cellIdentity0.networkId = NETWORK_ID;
+        cellIdentity0.systemId = SYSTEM_ID;
+        cellIdentity0.baseStationId = BASESTATION_ID;
+        cellIdentity0.longitude = LONGITUDE;
+        cellIdentity0.latitude = LATITUDE;
+
+        android.hardware.radio.V1_2.CellIdentityCdma cellIdentity =
+                new android.hardware.radio.V1_2.CellIdentityCdma();
+        cellIdentity.base = cellIdentity0;
+        cellIdentity.operatorNames = getCellIdentityOperatorNames();
+
+        android.hardware.radio.V1_0.CdmaSignalStrength cdmaSignalStrength =
+                new android.hardware.radio.V1_0.CdmaSignalStrength();
+        cdmaSignalStrength.dbm = -DBM;
+        cdmaSignalStrength.ecio = -ECIO;
+
+        android.hardware.radio.V1_0.EvdoSignalStrength evdoSignalStrength =
+                new android.hardware.radio.V1_0.EvdoSignalStrength();
+        evdoSignalStrength.dbm = -DBM;
+        evdoSignalStrength.ecio = -ECIO;
+        evdoSignalStrength.signalNoiseRatio = SIGNAL_NOISE_RATIO;
+
+        android.hardware.radio.V1_2.CellInfoCdma cellInfo =
+                new android.hardware.radio.V1_2.CellInfoCdma();
+        cellInfo.cellIdentityCdma = cellIdentity;
+        cellInfo.signalStrengthCdma = cdmaSignalStrength;
+        cellInfo.signalStrengthEvdo = evdoSignalStrength;
+
+        return cellInfo;
+    }
+
+    @Test
+    public void testConvertHalCellInfoList_1_4ForCdma() {
+        android.hardware.radio.V1_4.CellInfo cellInfo = new android.hardware.radio.V1_4.CellInfo();
+        cellInfo.isRegistered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.info.cdma(getCellInfoCdma_1_2());
+
+        ArrayList<Object> records = new ArrayList<>();
+        records.add(cellInfo);
 
         ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
 
         assertEquals(1, ret.size());
         CellInfoCdma cellInfoCdma = (CellInfoCdma) ret.get(0);
-        CellInfoCdma expected = new CellInfoCdma();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityCdma ci = new CellIdentityCdma(
-                NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
-                EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
-        CellSignalStrengthCdma cs = new CellSignalStrengthCdma(
-                DBM, ECIO, DBM, ECIO, SIGNAL_NOISE_RATIO);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
         cellInfoCdma.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        CellIdentityCdma cellIdentityCdma = new CellIdentityCdma(NETWORK_ID, SYSTEM_ID,
+                BASESTATION_ID, LONGITUDE, LATITUDE, ALPHA_LONG, ALPHA_SHORT);
+        CellSignalStrengthCdma cellSignalStrengthCdma = new CellSignalStrengthCdma(
+                DBM, ECIO, DBM, ECIO, SIGNAL_NOISE_RATIO);
+        CellInfoCdma expected = new CellInfoCdma(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityCdma, cellSignalStrengthCdma);
         assertEquals(expected, cellInfoCdma);
     }
 
     @Test
-    public void testConvertHalCellInfoList_1_2ForLTE() {
-        ArrayList<CellInfo> ret = getCellInfoListForLTE(MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
+    public void testConvertHalCellInfoList_1_5ForCdma() {
+        android.hardware.radio.V1_5.CellInfo cellInfo = new android.hardware.radio.V1_5.CellInfo();
+        cellInfo.registered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.ratSpecificInfo.cdma(getCellInfoCdma_1_2());
 
-        assertEquals(1, ret.size());
-        CellInfoLte cellInfoLte = (CellInfoLte) ret.get(0);
-        CellInfoLte expected = new CellInfoLte();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityLte cil = new CellIdentityLte(
-                CI, PCI, TAC, EARFCN, new int[] {}, BANDWIDTH, MCC_STR, MNC_STR,
-                ALPHA_LONG, ALPHA_SHORT, Collections.emptyList(), null);
-        CellSignalStrengthLte css = new CellSignalStrengthLte(
-                RSSI, RSRP, RSRQ, RSSNR, CQI, TIMING_ADVANCE);
-        expected.setCellIdentity(cil);
-        expected.setCellSignalStrength(css);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
-        cellInfoLte.setTimeStamp(TIMESTAMP); // override the timestamp
-        assertEquals(expected, cellInfoLte);
-    }
+        ArrayList<Object> records = new ArrayList<>();
+        records.add(cellInfo);
 
-    @Test
-    public void testConvertHalCellInfoList_1_2_ForLTEWithEmptyOperatorInfo() {
-        ArrayList<CellInfo> ret = getCellInfoListForLTE(
-                MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
-
-        assertEquals(1, ret.size());
-        CellInfoLte cellInfoLte = (CellInfoLte) ret.get(0);
-        CellInfoLte expected = new CellInfoLte();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityLte cil = new CellIdentityLte(CI, PCI, TAC, EARFCN, new int[] {},
-                BANDWIDTH, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT,
-                Collections.emptyList(), null);
-        CellSignalStrengthLte css = new CellSignalStrengthLte(
-                RSSI, RSRP, RSRQ, RSSNR, CQI, TIMING_ADVANCE);
-        expected.setCellIdentity(cil);
-        expected.setCellSignalStrength(css);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
-        cellInfoLte.setTimeStamp(TIMESTAMP); // override the timestamp
-        assertEquals(expected, cellInfoLte);
-    }
-
-    @Test
-    public void testConvertHalCellInfoList_1_2ForLTEWithEmptyMccMnc() {
-        // MCC/MNC will be set as INT_MAX if unknown
-        ArrayList<CellInfo> ret = getCellInfoListForLTE(
-                String.valueOf(Integer.MAX_VALUE), String.valueOf(Integer.MAX_VALUE),
-                ALPHA_LONG, ALPHA_SHORT);
-
-        assertEquals(1, ret.size());
-        CellInfoLte cellInfoLte = (CellInfoLte) ret.get(0);
-        CellInfoLte expected = new CellInfoLte();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityLte cil = new CellIdentityLte(
-                CI, PCI, TAC, EARFCN, new int[] {}, BANDWIDTH, null, null, ALPHA_LONG,
-                ALPHA_SHORT, Collections.emptyList(), null);
-        CellSignalStrengthLte css = new CellSignalStrengthLte(
-                RSSI, RSRP, RSRQ, RSSNR, CQI, TIMING_ADVANCE);
-        expected.setCellIdentity(cil);
-        expected.setCellSignalStrength(css);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
-        cellInfoLte.setTimeStamp(TIMESTAMP); // override the timestamp
-        assertEquals(expected, cellInfoLte);
-    }
-
-    @Test
-    public void testConvertHalCellInfoList_1_2ForGSM() {
-        ArrayList<CellInfo> ret = getCellInfoListForGSM(MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
-
-        assertEquals(1, ret.size());
-        CellInfoGsm cellInfoGsm = (CellInfoGsm) ret.get(0);
-        CellInfoGsm expected = new CellInfoGsm();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityGsm ci = new CellIdentityGsm(
-                LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT,
-                Collections.emptyList());
-        CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
-                RSSI, BIT_ERROR_RATE, TIMING_ADVANCE);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
-        cellInfoGsm.setTimeStamp(TIMESTAMP); // override the timestamp
-        assertEquals(expected, cellInfoGsm);
-    }
-
-    @Test
-    public void testConvertHalCellInfoList_1_2ForGSMWithEmptyOperatorInfo() {
-        ArrayList<CellInfo> ret = getCellInfoListForGSM(
-                MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
-
-        assertEquals(1, ret.size());
-        CellInfoGsm cellInfoGsm = (CellInfoGsm) ret.get(0);
-        CellInfoGsm expected = new CellInfoGsm();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityGsm ci = new CellIdentityGsm(
-                LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT,
-                Collections.emptyList());
-        CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
-                RSSI, BIT_ERROR_RATE, TIMING_ADVANCE);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
-        cellInfoGsm.setTimeStamp(TIMESTAMP); // override the timestamp
-        assertEquals(expected, cellInfoGsm);
-    }
-
-    @Test
-    public void testConvertHalCellInfoList_1_2ForGSMWithEmptyMccMnc() {
-        // MCC/MNC will be set as INT_MAX if unknown
-        ArrayList<CellInfo> ret = getCellInfoListForGSM(
-                String.valueOf(Integer.MAX_VALUE), String.valueOf(Integer.MAX_VALUE),
-                ALPHA_LONG, ALPHA_SHORT);
-
-        assertEquals(1, ret.size());
-        CellInfoGsm cellInfoGsm = (CellInfoGsm) ret.get(0);
-        CellInfoGsm expected = new CellInfoGsm();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityGsm ci = new CellIdentityGsm(
-                LAC, CID, ARFCN, BSIC, null, null, ALPHA_LONG, ALPHA_SHORT,
-                Collections.emptyList());
-        CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
-                RSSI, BIT_ERROR_RATE, TIMING_ADVANCE);
-        expected.setCellIdentity(ci);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
-        expected.setCellSignalStrength(cs);
-        cellInfoGsm.setTimeStamp(TIMESTAMP); // override the timestamp
-        assertEquals(expected, cellInfoGsm);
-    }
-
-    @Test
-    public void testConvertHalCellInfoList_1_2ForWcdma() {
-        ArrayList<CellInfo> ret = getCellInfoListForWcdma(
-                MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
-
-        assertEquals(1, ret.size());
-        CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ret.get(0);
-        CellInfoWcdma expected = new CellInfoWcdma();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityWcdma ci = new CellIdentityWcdma(
-                LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT,
-                Collections.emptyList(), null);
-        CellSignalStrengthWcdma cs =
-                new CellSignalStrengthWcdma(RSSI, BIT_ERROR_RATE, RSCP, ECNO);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
-        cellInfoWcdma.setTimeStamp(TIMESTAMP); // override the timestamp
-        assertEquals(expected, cellInfoWcdma);
-    }
-
-    @Test
-    public void testConvertHalCellInfoList_1_2ForWcdmaWithEmptyOperatorInfo() {
-        ArrayList<CellInfo> ret = getCellInfoListForWcdma(
-                MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
-
-        assertEquals(1, ret.size());
-        CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ret.get(0);
-        CellInfoWcdma expected = new CellInfoWcdma();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityWcdma ci = new CellIdentityWcdma(
-                LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT,
-                Collections.emptyList(), null);
-        CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
-                RSSI, BIT_ERROR_RATE, RSCP, ECNO);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
-        cellInfoWcdma.setTimeStamp(TIMESTAMP); // override the timestamp
-        assertEquals(expected, cellInfoWcdma);
-    }
-
-    @Test
-    public void testConvertHalCellInfoList_1_2ForWcdmaWithEmptyMccMnc() {
-        // MCC/MNC will be set as INT_MAX if unknown
-        ArrayList<CellInfo> ret = getCellInfoListForWcdma(null, null, ALPHA_LONG, ALPHA_SHORT);
-
-        assertEquals(1, ret.size());
-        CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ret.get(0);
-        CellInfoWcdma expected = new CellInfoWcdma();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityWcdma ci = new CellIdentityWcdma(
-                LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT,
-                Collections.emptyList(), null);
-        CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
-                RSSI, BIT_ERROR_RATE, RSCP, ECNO);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
-        cellInfoWcdma.setTimeStamp(TIMESTAMP); // override the timestamp
-        assertEquals(expected, cellInfoWcdma);
-    }
-
-    @Test
-    public void testConvertHalCellInfoList_1_2ForCdma() {
-        ArrayList<CellInfo> ret = getCellInfoListForCdma(ALPHA_LONG, ALPHA_SHORT);
+        ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
 
         assertEquals(1, ret.size());
         CellInfoCdma cellInfoCdma = (CellInfoCdma) ret.get(0);
-        CellInfoCdma expected = new CellInfoCdma();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityCdma ci = new CellIdentityCdma(
-                NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
-                ALPHA_LONG, ALPHA_SHORT);
-        CellSignalStrengthCdma cs = new CellSignalStrengthCdma(
-                DBM, ECIO, DBM, ECIO, SIGNAL_NOISE_RATIO);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
         cellInfoCdma.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        CellIdentityCdma cellIdentityCdma = new CellIdentityCdma(NETWORK_ID, SYSTEM_ID,
+                BASESTATION_ID, LONGITUDE, LATITUDE, ALPHA_LONG, ALPHA_SHORT);
+        CellSignalStrengthCdma cellSignalStrengthCdma = new CellSignalStrengthCdma(
+                DBM, ECIO, DBM, ECIO, SIGNAL_NOISE_RATIO);
+        CellInfoCdma expected = new CellInfoCdma(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityCdma, cellSignalStrengthCdma);
         assertEquals(expected, cellInfoCdma);
     }
 
     @Test
-    public void testConvertHalCellInfoList_1_2ForCdmaWithEmptyOperatorInfo() {
-        ArrayList<CellInfo> ret = getCellInfoListForCdma(EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+    public void testConvertHalCellInfoList_1_6ForCdma() {
+        android.hardware.radio.V1_6.CellInfo cellInfo = new android.hardware.radio.V1_6.CellInfo();
+        cellInfo.registered = REGISTERED;
+        cellInfo.connectionStatus = CONNECTION_STATUS;
+        cellInfo.ratSpecificInfo.cdma(getCellInfoCdma_1_2());
+
+        ArrayList<Object> records = new ArrayList<>();
+        records.add(cellInfo);
+
+        ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(records);
 
         assertEquals(1, ret.size());
         CellInfoCdma cellInfoCdma = (CellInfoCdma) ret.get(0);
-        CellInfoCdma expected = new CellInfoCdma();
-        expected.setRegistered(false);
-        expected.setTimeStamp(TIMESTAMP);
-        CellIdentityCdma ci = new CellIdentityCdma(
-                NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
-                EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
-        CellSignalStrengthCdma cs = new CellSignalStrengthCdma(
-                DBM, ECIO, DBM, ECIO, SIGNAL_NOISE_RATIO);
-        expected.setCellIdentity(ci);
-        expected.setCellSignalStrength(cs);
-        expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
         cellInfoCdma.setTimeStamp(TIMESTAMP); // override the timestamp
+
+        CellIdentityCdma cellIdentityCdma = new CellIdentityCdma(NETWORK_ID, SYSTEM_ID,
+                BASESTATION_ID, LONGITUDE, LATITUDE, ALPHA_LONG, ALPHA_SHORT);
+        CellSignalStrengthCdma cellSignalStrengthCdma = new CellSignalStrengthCdma(
+                DBM, ECIO, DBM, ECIO, SIGNAL_NOISE_RATIO);
+        CellInfoCdma expected = new CellInfoCdma(CONNECTION_STATUS, REGISTERED, TIMESTAMP,
+                cellIdentityCdma, cellSignalStrengthCdma);
         assertEquals(expected, cellInfoCdma);
     }
 
@@ -2105,170 +2278,8 @@
         assertEquals(expectedSignalStrength, signalStrengthNr);
     }
 
-    private static android.hardware.radio.V1_5.ClosedSubscriberGroupInfo getHalCsgInfo() {
-        android.hardware.radio.V1_5.ClosedSubscriberGroupInfo csgInfo =
-                new android.hardware.radio.V1_5.ClosedSubscriberGroupInfo();
-
-        csgInfo.csgIndication = CSG_INDICATION;
-        csgInfo.homeNodebName = HOME_NODEB_NAME;
-        csgInfo.csgIdentity = CSG_IDENTITY;
-
-        return csgInfo;
-    }
-
-    private static void initializeCellIdentityLte_1_5(
-            android.hardware.radio.V1_5.CellIdentityLte id,
-            boolean addAdditionalPlmns, boolean addCsgInfo) {
-
-        initializeCellIdentityLte_1_2(id.base);
-
-        if (addAdditionalPlmns) {
-            id.additionalPlmns = new ArrayList<>(
-                    Arrays.asList(ADDITIONAL_PLMNS));
-        }
-
-        if (addCsgInfo) {
-            id.optionalCsgInfo.csgInfo(getHalCsgInfo());
-        }
-    }
-
-    @Test
-    public void testCellIdentityLte_1_5_CsgInfo() {
-        android.hardware.radio.V1_5.CellIdentityLte halCellIdentity =
-                new android.hardware.radio.V1_5.CellIdentityLte();
-        initializeCellIdentityLte_1_5(halCellIdentity, false, true);
-
-        CellIdentityLte cellIdentity = RILUtils.convertHalCellIdentityLte(halCellIdentity);
-
-        assertEquals(CSG_INDICATION,
-                cellIdentity.getClosedSubscriberGroupInfo().getCsgIndicator());
-        assertEquals(HOME_NODEB_NAME,
-                cellIdentity.getClosedSubscriberGroupInfo().getHomeNodebName());
-        assertEquals(CSG_IDENTITY,
-                cellIdentity.getClosedSubscriberGroupInfo().getCsgIdentity());
-    }
-
-    @Test
-    public void testCellIdentityLte_1_5_MultiPlmn() {
-        android.hardware.radio.V1_5.CellIdentityLte halCellIdentity =
-                new android.hardware.radio.V1_5.CellIdentityLte();
-        initializeCellIdentityLte_1_5(halCellIdentity, true, false);
-
-        CellIdentityLte cellIdentity = RILUtils.convertHalCellIdentityLte(halCellIdentity);
-
-        Set<String> additionalPlmns = new HashSet<>();
-        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
-
-        assertEquals(cellIdentity.getAdditionalPlmns(), additionalPlmns);
-    }
-
-    private static void initializeCellIdentityWcdma_1_5(
-            android.hardware.radio.V1_5.CellIdentityWcdma id,
-            boolean addAdditionalPlmns, boolean addCsgInfo) {
-
-        initializeCellIdentityWcdma_1_2(id.base);
-
-        if (addAdditionalPlmns) {
-            id.additionalPlmns = new ArrayList<>(Arrays.asList(ADDITIONAL_PLMNS));
-        }
-
-        if (addCsgInfo) {
-            id.optionalCsgInfo.csgInfo(getHalCsgInfo());
-        }
-    }
-
-    @Test
-    public void testCellIdentityWcdma_1_5_CsgInfo() {
-        android.hardware.radio.V1_5.CellIdentityWcdma halCellIdentity =
-                new android.hardware.radio.V1_5.CellIdentityWcdma();
-        initializeCellIdentityWcdma_1_5(halCellIdentity, false, true);
-
-        CellIdentityWcdma cellIdentity = RILUtils.convertHalCellIdentityWcdma(halCellIdentity);
-
-        assertEquals(CSG_INDICATION,
-                cellIdentity.getClosedSubscriberGroupInfo().getCsgIndicator());
-        assertEquals(HOME_NODEB_NAME,
-                cellIdentity.getClosedSubscriberGroupInfo().getHomeNodebName());
-        assertEquals(CSG_IDENTITY,
-                cellIdentity.getClosedSubscriberGroupInfo().getCsgIdentity());
-    }
-
-    @Test
-    public void testCellIdentityWcdma_1_5_MultiPlmn() {
-        android.hardware.radio.V1_5.CellIdentityWcdma halCellIdentity =
-                new android.hardware.radio.V1_5.CellIdentityWcdma();
-        initializeCellIdentityWcdma_1_5(halCellIdentity, true, false);
-
-        CellIdentityWcdma cellIdentity = RILUtils.convertHalCellIdentityWcdma(halCellIdentity);
-
-        Set<String> additionalPlmns = new HashSet<>();
-        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
-
-        assertEquals(cellIdentity.getAdditionalPlmns(), additionalPlmns);
-    }
-
-    private static void initializeCellIdentityTdscdma_1_5(
-            android.hardware.radio.V1_5.CellIdentityTdscdma id,
-            boolean addAdditionalPlmns, boolean addCsgInfo) {
-
-        initializeCellIdentityTdscdma_1_2(id.base);
-
-        if (addAdditionalPlmns) {
-            id.additionalPlmns = new ArrayList<>(Arrays.asList(ADDITIONAL_PLMNS));
-        }
-
-        if (addCsgInfo) {
-            id.optionalCsgInfo.csgInfo(getHalCsgInfo());
-        }
-    }
-
-    @Test
-    public void testCellIdentityTdscdma_1_5_CsgInfo() {
-        android.hardware.radio.V1_5.CellIdentityTdscdma halCellIdentity =
-                new android.hardware.radio.V1_5.CellIdentityTdscdma();
-        initializeCellIdentityTdscdma_1_5(halCellIdentity, false, true);
-
-        CellIdentityTdscdma cellIdentity = RILUtils.convertHalCellIdentityTdscdma(halCellIdentity);
-
-        assertEquals(CSG_INDICATION,
-                cellIdentity.getClosedSubscriberGroupInfo().getCsgIndicator());
-        assertEquals(HOME_NODEB_NAME,
-                cellIdentity.getClosedSubscriberGroupInfo().getHomeNodebName());
-        assertEquals(CSG_IDENTITY,
-                cellIdentity.getClosedSubscriberGroupInfo().getCsgIdentity());
-    }
-
-    @Test
-    public void testCellIdentityTdscdma_1_5_MultiPlmn() {
-        android.hardware.radio.V1_5.CellIdentityTdscdma halCellIdentity =
-                new android.hardware.radio.V1_5.CellIdentityTdscdma();
-        initializeCellIdentityTdscdma_1_5(halCellIdentity, true, false);
-
-        CellIdentityTdscdma cellIdentity = RILUtils.convertHalCellIdentityTdscdma(halCellIdentity);
-
-        Set<String> additionalPlmns = new HashSet<>();
-        Collections.addAll(additionalPlmns, ADDITIONAL_PLMNS);
-
-        assertEquals(cellIdentity.getAdditionalPlmns(), additionalPlmns);
-    }
-
     @Test
     public void testConvertDataCallResult() {
-        // Test V1.0 SetupDataCallResult
-        android.hardware.radio.V1_0.SetupDataCallResult result10 =
-                new android.hardware.radio.V1_0.SetupDataCallResult();
-        result10.status = android.hardware.radio.V1_0.DataCallFailCause.NONE;
-        result10.suggestedRetryTime = -1;
-        result10.cid = 0;
-        result10.active = 2;
-        result10.type = "IPV4V6";
-        result10.ifname = "ifname";
-        result10.addresses = "10.0.2.15 2607:fb90:a620:651d:eabe:f8da:c107:44be/64";
-        result10.dnses = "10.0.2.3 fd00:976a::9";
-        result10.gateways = "10.0.2.15 fe80::2";
-        result10.pcscf = "fd00:976a:c206:20::6   fd00:976a:c206:20::9    fd00:976a:c202:1d::9";
-        result10.mtu = 1500;
-
         DataCallResponse response = new DataCallResponse.Builder()
                 .setCause(0)
                 .setRetryDurationMillis(-1L)
@@ -2294,8 +2305,6 @@
                 .setTrafficDescriptors(new ArrayList<>())
                 .build();
 
-        assertEquals(response, RILUtils.convertHalDataCallResult(result10));
-
         // Test V1.4 SetupDataCallResult
         android.hardware.radio.V1_4.SetupDataCallResult result14 =
                 new android.hardware.radio.V1_4.SetupDataCallResult();
@@ -2532,11 +2541,7 @@
         ArrayList<Object> records = new ArrayList<>();
 
         for (int i = 0; i < 5 /* arbitrary */; i++) {
-            android.hardware.radio.V1_4.CellInfo record =
-                    new android.hardware.radio.V1_4.CellInfo();
-            record.info = new android.hardware.radio.V1_4.CellInfo.Info();
-            record.info.lte(new android.hardware.radio.V1_4.CellInfoLte());
-            initializeCellInfoLte_1_2(record.info.lte().base);
+            android.hardware.radio.V1_4.CellInfo record = getCellInfo_1_4ForLte();
             record.info.lte().base.cellIdentityLte.base.ci += i; // make them marginally unique
 
             records.add(record);
@@ -2552,181 +2557,6 @@
     }
 
     @Test
-    public void testCellInfoTimestamp_1_2() {
-        ArrayList<Object> records = new ArrayList<>();
-
-        for (int i = 0; i < 5 /* arbitrary */; i++) {
-            android.hardware.radio.V1_2.CellInfo record =
-                    new android.hardware.radio.V1_2.CellInfo();
-            record.cellInfoType = TYPE_LTE;
-            record.timeStamp = Long.MAX_VALUE;
-            record.registered = false;
-            record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
-            record.lte.add(new android.hardware.radio.V1_2.CellInfoLte());
-            initializeCellInfoLte_1_2(record.lte.get(0));
-            record.lte.get(0).cellIdentityLte.base.ci += i; // make them marginally unique
-
-            records.add(record);
-        }
-        List<CellInfo> cil = RILUtils.convertHalCellInfoList(records);
-
-        // Check that all timestamps are set to a valid number and are equal
-        final long ts = cil.get(0).getTimeStamp();
-        for (CellInfo ci : cil) {
-            assertTrue(ci.getTimeStamp() > 0 && ci.getTimeStamp() != Long.MAX_VALUE);
-            assertEquals(ci.getTimeStamp(), ts);
-        }
-    }
-
-    private static void initializeCellIdentityLte_1_2(
-            android.hardware.radio.V1_2.CellIdentityLte id) {
-        // 1.0 fields
-        id.base.mcc = MCC_STR;
-        id.base.mnc = MNC_STR;
-        id.base.ci = CI;
-        id.base.pci = PCI;
-        id.base.tac = TAC;
-        id.base.earfcn = EARFCN;
-
-        // 1.2 fields
-        id.bandwidth = BANDWIDTH;
-        id.operatorNames.alphaLong = ALPHA_LONG;
-        id.operatorNames.alphaShort = ALPHA_SHORT;
-    }
-
-    private static void initializeCellInfoLte_1_2(android.hardware.radio.V1_2.CellInfoLte lte) {
-        initializeCellIdentityLte_1_2(lte.cellIdentityLte);
-
-        lte.signalStrengthLte.signalStrength = RSSI_ASU;
-        lte.signalStrengthLte.rsrp = -RSRP;
-        lte.signalStrengthLte.rsrq = -RSRQ;
-        lte.signalStrengthLte.rssnr = RSSNR;
-        lte.signalStrengthLte.cqi = CQI;
-        lte.signalStrengthLte.timingAdvance = TIMING_ADVANCE;
-    }
-
-    private ArrayList<CellInfo> getCellInfoListForLTE(
-            String mcc, String mnc, String alphaLong, String alphaShort) {
-        android.hardware.radio.V1_2.CellInfoLte lte = new android.hardware.radio.V1_2.CellInfoLte();
-
-        initializeCellInfoLte_1_2(lte);
-        // Override the defaults for test-specific purposes
-        lte.cellIdentityLte.operatorNames.alphaLong = alphaLong;
-        lte.cellIdentityLte.operatorNames.alphaShort = alphaShort;
-        lte.cellIdentityLte.base.mcc = mcc;
-        lte.cellIdentityLte.base.mnc = mnc;
-
-        android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
-        record.cellInfoType = TYPE_LTE;
-        record.registered = false;
-        record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
-        record.timeStamp = TIMESTAMP;
-        record.lte.add(lte);
-        record.connectionStatus = 0;
-        ArrayList<Object> records = new ArrayList<>();
-        records.add(record);
-        return RILUtils.convertHalCellInfoList(records);
-    }
-
-    private ArrayList<CellInfo> getCellInfoListForGSM(
-            String mcc, String mnc, String alphaLong, String alphaShort) {
-        android.hardware.radio.V1_2.CellInfoGsm cellinfo =
-                new android.hardware.radio.V1_2.CellInfoGsm();
-        cellinfo.cellIdentityGsm.base.lac = LAC;
-        cellinfo.cellIdentityGsm.base.cid = CID;
-        cellinfo.cellIdentityGsm.base.bsic = BSIC;
-        cellinfo.cellIdentityGsm.base.arfcn = ARFCN;
-        cellinfo.cellIdentityGsm.base.mcc = mcc;
-        cellinfo.cellIdentityGsm.base.mnc = mnc;
-        cellinfo.cellIdentityGsm.operatorNames.alphaLong = alphaLong;
-        cellinfo.cellIdentityGsm.operatorNames.alphaShort = alphaShort;
-        cellinfo.signalStrengthGsm.signalStrength = RSSI_ASU;
-        cellinfo.signalStrengthGsm.bitErrorRate = BIT_ERROR_RATE;
-        cellinfo.signalStrengthGsm.timingAdvance = TIMING_ADVANCE;
-        android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
-        record.cellInfoType = TYPE_GSM;
-        record.registered = false;
-        record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
-        record.timeStamp = TIMESTAMP;
-        record.gsm.add(cellinfo);
-        record.connectionStatus = 0;
-        ArrayList<Object> records = new ArrayList<>();
-        records.add(record);
-
-        return RILUtils.convertHalCellInfoList(records);
-    }
-
-    private static void initializeCellIdentityWcdma_1_2(
-            android.hardware.radio.V1_2.CellIdentityWcdma cid) {
-        initializeCellIdentityWcdma_1_2(cid, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
-    }
-
-    private static void initializeCellIdentityWcdma_1_2(
-            android.hardware.radio.V1_2.CellIdentityWcdma cid,
-                String mcc, String mnc, String alphaLong, String alphaShort) {
-        cid.base.lac = LAC;
-        cid.base.cid = CID;
-        cid.base.psc = PSC;
-        cid.base.uarfcn = UARFCN;
-        cid.base.mcc = mcc;
-        cid.base.mnc = mnc;
-        cid.operatorNames.alphaLong = alphaLong;
-        cid.operatorNames.alphaShort = alphaShort;
-    }
-
-    private ArrayList<CellInfo> getCellInfoListForWcdma(
-            String mcc, String mnc, String alphaLong, String alphaShort) {
-        android.hardware.radio.V1_2.CellInfoWcdma cellinfo =
-                new android.hardware.radio.V1_2.CellInfoWcdma();
-        initializeCellIdentityWcdma_1_2(
-                cellinfo.cellIdentityWcdma, mcc, mnc, alphaLong, alphaShort);
-
-        cellinfo.signalStrengthWcdma.base.signalStrength = RSSI_ASU;
-        cellinfo.signalStrengthWcdma.base.bitErrorRate = BIT_ERROR_RATE;
-        cellinfo.signalStrengthWcdma.rscp = RSCP_ASU;
-        cellinfo.signalStrengthWcdma.ecno = ECNO_ASU;
-        android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
-        record.cellInfoType = TYPE_WCDMA;
-        record.registered = false;
-        record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
-        record.timeStamp = TIMESTAMP;
-        record.wcdma.add(cellinfo);
-        record.connectionStatus = 0;
-        ArrayList<Object> records = new ArrayList<>();
-        records.add(record);
-
-        return RILUtils.convertHalCellInfoList(records);
-    }
-
-    private ArrayList<CellInfo> getCellInfoListForCdma(String alphaLong, String alphaShort) {
-        android.hardware.radio.V1_2.CellInfoCdma cellinfo =
-                new android.hardware.radio.V1_2.CellInfoCdma();
-        cellinfo.cellIdentityCdma.base.networkId = NETWORK_ID;
-        cellinfo.cellIdentityCdma.base.systemId = SYSTEM_ID;
-        cellinfo.cellIdentityCdma.base.baseStationId = BASESTATION_ID;
-        cellinfo.cellIdentityCdma.base.longitude = LONGITUDE;
-        cellinfo.cellIdentityCdma.base.latitude = LATITUDE;
-        cellinfo.cellIdentityCdma.operatorNames.alphaLong = alphaLong;
-        cellinfo.cellIdentityCdma.operatorNames.alphaShort = alphaShort;
-        cellinfo.signalStrengthCdma.dbm = -DBM;
-        cellinfo.signalStrengthCdma.ecio = -ECIO;
-        cellinfo.signalStrengthEvdo.dbm = -DBM;
-        cellinfo.signalStrengthEvdo.ecio = -ECIO;
-        cellinfo.signalStrengthEvdo.signalNoiseRatio = SIGNAL_NOISE_RATIO;
-        android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
-        record.cellInfoType = TYPE_CDMA;
-        record.registered = false;
-        record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
-        record.timeStamp = TIMESTAMP;
-        record.cdma.add(cellinfo);
-        record.connectionStatus = 0;
-        ArrayList<Object> records = new ArrayList<>();
-        records.add(record);
-
-        return RILUtils.convertHalCellInfoList(records);
-    }
-
-    @Test
     public void testSetupDataCall() throws Exception {
         ApnSetting apn = new ApnSetting.Builder()
                 .setId(1234)
@@ -2750,13 +2580,12 @@
                 .setPreferred(false)
                 .build();
 
-        mRILUnderTest.setupDataCall(AccessNetworkConstants.AccessNetworkType.EUTRAN, dp, false,
-                false, 0, null, DataCallResponse.PDU_SESSION_ID_NOT_SET, null, null, true,
-                obtainMessage());
+        mRILUnderTest.setupDataCall(AccessNetworkConstants.AccessNetworkType.EUTRAN, dp, false, 0,
+                null, DataCallResponse.PDU_SESSION_ID_NOT_SET, null, null, true, obtainMessage());
         ArgumentCaptor<DataProfile> dpiCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mDataProxy).setupDataCall(mSerialNumberCaptor.capture(),
-                anyInt(), eq(AccessNetworkConstants.AccessNetworkType.EUTRAN), dpiCaptor.capture(),
-                eq(false), eq(false), anyInt(), any(), anyInt(), any(), any(), eq(true));
+                eq(AccessNetworkConstants.AccessNetworkType.EUTRAN), dpiCaptor.capture(),
+                eq(false), anyInt(), any(), anyInt(), any(), any(), eq(true));
         verifyRILResponse(
                 mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SETUP_DATA_CALL);
         DataProfile dpi = dpiCaptor.getValue();
@@ -2775,46 +2604,6 @@
     }
 
     @Test
-    public void testFixupSignalStrength10() {
-        final int gsmWcdmaRssiDbm = -65;
-
-        // Test the positive case where rat=UMTS and SignalStrength=GSM
-        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS)
-                .when(mServiceState).getRilVoiceRadioTechnology();
-
-        SignalStrength gsmSignalStrength = new SignalStrength(
-                new CellSignalStrengthCdma(),
-                new CellSignalStrengthGsm(gsmWcdmaRssiDbm, 1, CellInfo.UNAVAILABLE),
-                new CellSignalStrengthWcdma(), new CellSignalStrengthTdscdma(),
-                new CellSignalStrengthLte(), new CellSignalStrengthNr());
-        SignalStrength result = mRILUnderTest.fixupSignalStrength10(gsmSignalStrength);
-
-        assertTrue(result.getCellSignalStrengths(CellSignalStrengthGsm.class).isEmpty());
-        assertFalse(result.getCellSignalStrengths(CellSignalStrengthWcdma.class).isEmpty());
-
-        // Even though the dBm values are equal, the above checks ensure that the value has
-        // been migrated to WCDMA (with no change in the top-level getDbm() result).
-        assertEquals(result.getDbm(), gsmSignalStrength.getDbm());
-
-        // Test the no-op case where rat=GSM and SignalStrength=GSM
-        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_GSM)
-                .when(mServiceState).getRilVoiceRadioTechnology();
-        result = mRILUnderTest.fixupSignalStrength10(gsmSignalStrength);
-        assertEquals(result, gsmSignalStrength);
-
-        // Check that non-GSM non-WCDMA signal strengths are also passed through.
-        SignalStrength lteSignalStrength = new SignalStrength(
-                new CellSignalStrengthCdma(), new CellSignalStrengthGsm(),
-                new CellSignalStrengthWcdma(), new CellSignalStrengthTdscdma(),
-                new CellSignalStrengthLte(CellInfo.UNAVAILABLE,
-                        -120, -10, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
-                        CellInfo.UNAVAILABLE), new CellSignalStrengthNr());
-        SignalStrength lteResult = mRILUnderTest.fixupSignalStrength10(lteSignalStrength);
-
-        assertEquals(lteResult, lteSignalStrength);
-    }
-
-    @Test
     public void testCreateCarrierRestrictionList() {
         ArrayList<CarrierIdentifier> carriers = new ArrayList<>();
         carriers.add(new CarrierIdentifier("110", "120", null, null, null, null));
@@ -2857,7 +2646,7 @@
 
         ArrayList<Carrier> result = RILUtils.convertToHalCarrierRestrictionList(carriers);
 
-        assertTrue(result.equals(expected));
+        assertEquals(result, expected);
     }
 
     @Test
@@ -2897,34 +2686,34 @@
     @Test
     public void testAreUiccApplicationsEnabled_nullRadioProxy() throws Exception {
         // Not supported on Radio 1.0.
-        doReturn(null).when(mRILUnderTest).getRadioProxy(any());
+        doReturn(null).when(mRILUnderTest).getRadioProxy();
         Message message = obtainMessage();
         mRILUnderTest.areUiccApplicationsEnabled(message);
         processAllMessages();
         verify(mSimProxy, never()).areUiccApplicationsEnabled(mSerialNumberCaptor.capture());
         // Sending message is handled by getRadioProxy when proxy is null.
         // areUiccApplicationsEnabled shouldn't explicitly send another callback.
-        assertEquals(null, message.obj);
+        assertNull(message.obj);
     }
 
     @Test
-    public void testSetGetCompatVersion() throws Exception {
+    public void testSetGetCompatVersion() {
         final int testRequest = RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT;
 
         // getCompactVersion should return null before first setting
         assertNull(mRILUnderTest.getCompatVersion(testRequest));
 
         // first time setting any valid HalVersion will success
+        mRILUnderTest.setCompatVersion(testRequest, RIL.RADIO_HAL_VERSION_1_5);
+        assertEquals(RIL.RADIO_HAL_VERSION_1_5, mRILUnderTest.getCompatVersion(testRequest));
+
+        // try to set a lower HalVersion will success
         mRILUnderTest.setCompatVersion(testRequest, RIL.RADIO_HAL_VERSION_1_4);
         assertEquals(RIL.RADIO_HAL_VERSION_1_4, mRILUnderTest.getCompatVersion(testRequest));
 
-        // try to set a lower HalVersion will success
-        mRILUnderTest.setCompatVersion(testRequest, RIL.RADIO_HAL_VERSION_1_3);
-        assertEquals(RIL.RADIO_HAL_VERSION_1_3, mRILUnderTest.getCompatVersion(testRequest));
-
         // try to set a greater HalVersion will not success
-        mRILUnderTest.setCompatVersion(testRequest, RIL.RADIO_HAL_VERSION_1_5);
-        assertEquals(RIL.RADIO_HAL_VERSION_1_3, mRILUnderTest.getCompatVersion(testRequest));
+        mRILUnderTest.setCompatVersion(testRequest, RIL.RADIO_HAL_VERSION_1_6);
+        assertEquals(RIL.RADIO_HAL_VERSION_1_4, mRILUnderTest.getCompatVersion(testRequest));
     }
 
     @FlakyTest
@@ -2963,8 +2752,61 @@
         Message message = obtainMessage();
         mRILUnderTest.getImei(message);
         AsyncResult ar = (AsyncResult) message.obj;
-        Assert.assertEquals(null, ar.result);
+        Assert.assertNull(ar.result);
         Assert.assertNotNull(ar.exception.getMessage());
         Assert.assertEquals("REQUEST_NOT_SUPPORTED", ar.exception.getMessage());
     }
+
+    @Test
+    public void testRadioServiceInvokeHelper() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        HandlerThread handlerThread = new HandlerThread("testRilServiceInvokeHelper");
+        handlerThread.start();
+        Handler handler = new Handler(handlerThread.getLooper()) {
+            public void handleMessage(Message msg) {
+                AsyncResult ar = (AsyncResult) msg.obj;
+                if (ar != null && ar.exception instanceof CommandException) {
+                    CommandException.Error err =
+                            ((CommandException) (ar.exception)).getCommandError();
+                    if (err == CommandException.Error.SYSTEM_ERR) {
+                        latch.countDown();
+                    }
+                }
+            }
+        };
+
+        // RuntimeException
+        doThrow(new RuntimeException()).when(mDataProxy).getDataCallList(anyInt());
+        mRILUnderTest.getDataCallList(handler.obtainMessage());
+        assertTrue(latch.await(3, TimeUnit.SECONDS));
+
+        // RemoteException
+        doThrow(new RemoteException()).when(mDataProxy).getDataCallList(anyInt());
+        mRILUnderTest.getDataCallList(handler.obtainMessage());
+        assertEquals(mRILUnderTest.getRadioState(), TelephonyManager.RADIO_POWER_UNAVAILABLE);
+    }
+
+
+    @Test
+    public void testRadioServiceNotAvailable() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        HandlerThread handlerThread = new HandlerThread("testRadioServiceNotAvailable");
+        handlerThread.start();
+        Handler handler = new Handler(handlerThread.getLooper()) {
+            public void handleMessage(Message msg) {
+                AsyncResult ar = (AsyncResult) msg.obj;
+                if (ar != null && ar.exception instanceof CommandException) {
+                    CommandException.Error err =
+                            ((CommandException) (ar.exception)).getCommandError();
+                    if (err == CommandException.Error.RADIO_NOT_AVAILABLE) {
+                        latch.countDown();
+                    }
+                }
+            }
+        };
+
+        when(mDataProxy.isEmpty()).thenReturn(true);
+        mRILUnderTest.getDataCallList(handler.obtainMessage());
+        assertTrue(latch.await(3, TimeUnit.SECONDS));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 5f592d1..0a15c47 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -257,7 +257,7 @@
         mSatelliteController = Mockito.mock(SatelliteController.class);
         replaceInstance(SatelliteController.class, "sInstance", null,
                 mSatelliteController);
-        doReturn(new ArrayList<>()).when(mSatelliteController).getSatellitePlmnList();
+        doReturn(new ArrayList<>()).when(mSatelliteController).getSatellitePlmnList(anyInt());
 
         mContextFixture.putResource(R.string.kg_text_message_separator, " \u2014 ");
 
@@ -3233,7 +3233,7 @@
         CellIdentityGsm cellIdentity =
                 new CellIdentityGsm(0, 1, 900, 5, "101", "23", "test", "tst",
                         Collections.emptyList());
-        doReturn(Arrays.asList("10123")).when(mSatelliteController).getSatellitePlmnList();
+        doReturn(Arrays.asList("10123")).when(mSatelliteController).getSatellitePlmnList(anyInt());
         doReturn(satelliteSupportedServiceList).when(mSatelliteController)
                 .getSupportedSatelliteServices(sst.mSubId, "10123");
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
index e4617fe..98980a1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
@@ -1012,6 +1012,263 @@
         assertThat(msgCaptor.getValue().what).isEqualTo(ssChangedEvent);
     }
 
+    @Test
+    public void testInvalidCarrierConfig_GERAN_RSSI_arrayIsTooLong() {
+        mBundle.putIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -109, /* SIGNAL_STRENGTH_POOR */
+                        -103, /* SIGNAL_STRENGTH_MODERATE */
+                        -97, /* SIGNAL_STRENGTH_GOOD */
+                        -89,  /* SIGNAL_STRENGTH_GREAT */
+                        -80, /* and extra value */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_GERAN_RSSI_arrayIsTooShort() {
+        mBundle.putIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY,
+                new int[]{});
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_GERAN_RSSI_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-113, -51]
+        mBundle.putIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -114, /* SIGNAL_STRENGTH_POOR */
+                        -103, /* SIGNAL_STRENGTH_MODERATE */
+                        -97, /* SIGNAL_STRENGTH_GOOD */
+                        -89,  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_GERAN_RSSI_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-113, -51]
+        mBundle.putIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -109, /* SIGNAL_STRENGTH_POOR */
+                        -103, /* SIGNAL_STRENGTH_MODERATE */
+                        -97, /* SIGNAL_STRENGTH_GOOD */
+                        -89,  /* SIGNAL_STRENGTH_GREAT */
+                        -50, /* and extra value */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_UTRAN_RSCP_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-120, -24]
+        mBundle.putIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -121, /* SIGNAL_STRENGTH_POOR */
+                        -104, /* SIGNAL_STRENGTH_MODERATE */
+                        -94,  /* SIGNAL_STRENGTH_GOOD */
+                        -84   /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_UTRAN_RSCP_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-120, -24]
+        mBundle.putIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -114, /* SIGNAL_STRENGTH_POOR */
+                        -104, /* SIGNAL_STRENGTH_MODERATE */
+                        -94,  /* SIGNAL_STRENGTH_GOOD */
+                        -23   /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_EUTRAN_RSRP_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-140, -44]
+        mBundle.putIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -141, /* SIGNAL_STRENGTH_POOR */
+                        -118, /* SIGNAL_STRENGTH_MODERATE */
+                        -108, /* SIGNAL_STRENGTH_GOOD */
+                        -98,  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_EUTRAN_RSRP_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-140, -44]
+        mBundle.putIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -128, /* SIGNAL_STRENGTH_POOR */
+                        -118, /* SIGNAL_STRENGTH_MODERATE */
+                        -108, /* SIGNAL_STRENGTH_GOOD */
+                        -43,  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_EUTRAN_RSRQ_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-34, 3]
+        mBundle.putIntArray(CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -35,  /* SIGNAL_STRENGTH_POOR */
+                        -17,  /* SIGNAL_STRENGTH_MODERATE */
+                        -14,  /* SIGNAL_STRENGTH_GOOD */
+                        -11   /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_EUTRAN_RSRQ_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-34, 3]
+        mBundle.putIntArray(CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -20,  /* SIGNAL_STRENGTH_POOR */
+                        -17,  /* SIGNAL_STRENGTH_MODERATE */
+                        -14,  /* SIGNAL_STRENGTH_GOOD */
+                        4   /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_EUTRAN_RSSNR_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-20, 30]
+        mBundle.putIntArray(CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -21,  /* SIGNAL_STRENGTH_POOR */
+                        1,   /* SIGNAL_STRENGTH_MODERATE */
+                        5,   /* SIGNAL_STRENGTH_GOOD */
+                        13   /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_EUTRAN_RSSNR_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-20, 30]
+        mBundle.putIntArray(CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -3,  /* SIGNAL_STRENGTH_POOR */
+                        1,   /* SIGNAL_STRENGTH_MODERATE */
+                        5,   /* SIGNAL_STRENGTH_GOOD */
+                        31   /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_NGRAN_SSRSRP_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-140, -44]
+        mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -141, /* SIGNAL_STRENGTH_POOR */
+                        -107, /* SIGNAL_STRENGTH_MODERATE */
+                        -100, /* SIGNAL_STRENGTH_GOOD */
+                        -95,  /* SIGNAL_STRENGTH_GREAT */
+                        -90, /* and extra value */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_NGRAN_SSRSRP_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-140, -44]
+        mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -113, /* SIGNAL_STRENGTH_POOR */
+                        -107, /* SIGNAL_STRENGTH_MODERATE */
+                        -100, /* SIGNAL_STRENGTH_GOOD */
+                        -95,  /* SIGNAL_STRENGTH_GREAT */
+                        -45, /* and extra value */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_NGRAN_SSRSRQ_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-43, 20]
+        mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -44, /* SIGNAL_STRENGTH_POOR */
+                        -19, /* SIGNAL_STRENGTH_MODERATE */
+                        -7, /* SIGNAL_STRENGTH_GOOD */
+                        6  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_NGRAN_SSRSRQ_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-43, 20]
+        mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -31, /* SIGNAL_STRENGTH_POOR */
+                        -19, /* SIGNAL_STRENGTH_MODERATE */
+                        -7, /* SIGNAL_STRENGTH_GOOD */
+                        21  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_NGRAN_SSSINR_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-23, 40]
+        mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -24, /* SIGNAL_STRENGTH_POOR */
+                        5, /* SIGNAL_STRENGTH_MODERATE */
+                        15, /* SIGNAL_STRENGTH_GOOD */
+                        30  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_NGRAN_SSSINR_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-24, 1]
+        mBundle.putIntArray(CarrierConfigManager.KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -25, /* SIGNAL_STRENGTH_POOR */
+                        -14, /* SIGNAL_STRENGTH_MODERATE */
+                        -6, /* SIGNAL_STRENGTH_GOOD */
+                        1  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_UTRAN_ECNO_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-24, 1]
+        mBundle.putIntArray(CarrierConfigManager.KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -24, /* SIGNAL_STRENGTH_POOR */
+                        -14, /* SIGNAL_STRENGTH_MODERATE */
+                        -6, /* SIGNAL_STRENGTH_GOOD */
+                        2  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_UTRAN_ECNO_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-23, 40]
+        mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -5, /* SIGNAL_STRENGTH_POOR */
+                        5, /* SIGNAL_STRENGTH_MODERATE */
+                        15, /* SIGNAL_STRENGTH_GOOD */
+                        41  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
     private void verifyAllEmptyThresholdAreDisabledWhenSetSignalStrengthReportingCriteria(
             int expectedNonEmptyThreshold) {
         ArgumentCaptor<List<SignalThresholdInfo>> signalThresholdInfoCaptor =
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
index 1e4c939..95a6154 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
@@ -18,9 +18,10 @@
 
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.radio.RadioError;
-import android.hardware.radio.V1_0.DataRegStateResult;
-import android.hardware.radio.V1_0.SetupDataCallResult;
-import android.hardware.radio.V1_0.VoiceRegStateResult;
+import android.hardware.radio.V1_2.VoiceRegStateResult;
+import android.hardware.radio.V1_4.DataRegStateResult;
+import android.hardware.radio.V1_4.PdpProtocolType;
+import android.hardware.radio.V1_4.SetupDataCallResult;
 import android.hardware.radio.modem.ImeiInfo;
 import android.net.KeepalivePacketData;
 import android.net.LinkProperties;
@@ -76,7 +77,6 @@
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
 import com.android.internal.telephony.uicc.IccCardStatus;
 import com.android.internal.telephony.uicc.IccIoResult;
-import com.android.internal.telephony.uicc.IccSlotStatus;
 import com.android.internal.telephony.uicc.ReceivedPhonebookRecords;
 import com.android.internal.telephony.uicc.SimPhonebookRecord;
 import com.android.telephony.Rlog;
@@ -89,7 +89,6 @@
 public class SimulatedCommands extends BaseCommands
         implements CommandsInterface, SimulatedRadioControl {
     private static final String LOG_TAG = "SimulatedCommands";
-    private boolean mSupportsEid = true;
 
     private enum SimLockState {
         NONE,
@@ -127,9 +126,6 @@
     // arrive and returning null to the callers.
     public static final  long ICC_SIM_CHALLENGE_TIMEOUT_MILLIS = 2500;
 
-    private String mImei;
-    private String mImeiSv;
-
     //***** Instance Variables
 
     @UnsupportedAppUsage
@@ -167,7 +163,6 @@
     private boolean mShouldReturnCellInfo = true;
     private int[] mImsRegState;
     private IccCardStatus mIccCardStatus;
-    private IccSlotStatus mIccSlotStatus;
     private IccIoResult mIccIoResultForApduLogicalChannel;
     private int mChannelId = IccOpenLogicalChannelResponse.INVALID_CHANNEL;
 
@@ -175,7 +170,7 @@
     private Object mVoiceRegStateResult;
 
     int mPausedResponseCount;
-    ArrayList<Message> mPausedResponses = new ArrayList<Message>();
+    ArrayList<Message> mPausedResponses = new ArrayList<>();
 
     int mNextCallFailCause = CallFailCause.NORMAL_CLEARING;
 
@@ -242,26 +237,6 @@
         }
     }
 
-    public void setIccSlotStatus(IccSlotStatus iccSlotStatus) {
-        mIccSlotStatus = iccSlotStatus;
-    }
-
-    @Override
-    public void getIccSlotsStatus(Message result) {
-        SimulatedCommandsVerifier.getInstance().getIccSlotsStatus(result);
-        if (mIccSlotStatus != null) {
-            resultSuccess(result, mIccSlotStatus);
-        } else {
-            resultFail(result, null,
-                    new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED));
-        }
-    }
-
-    @Override
-    public void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result) {
-        unimplemented(result);
-    }
-
     @Override
     public void supplyIccPin(String pin, Message result)  {
         if (mSimLockedState != SimLockState.REQUIRE_PIN) {
@@ -575,15 +550,6 @@
     }
 
     /**
-     *  @deprecated
-     */
-    @Deprecated
-    @Override
-    public void getPDPContextList(Message result) {
-        getDataCallList(result);
-    }
-
-    /**
      *  returned message
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
@@ -592,7 +558,7 @@
      */
     @Override
     public void getDataCallList(Message result) {
-        ArrayList<SetupDataCallResult> dcCallList = new ArrayList<SetupDataCallResult>(0);
+        ArrayList<SetupDataCallResult> dcCallList = new ArrayList<>(0);
         SimulatedCommandsVerifier.getInstance().getDataCallList(result);
         if (mSetupDataCallResult != null) {
             dcCallList.add(mSetupDataCallResult);
@@ -659,40 +625,6 @@
         resultSuccess(result, "012345678901234");
     }
 
-    public void setIMEI(String imei) {
-        mImei = imei;
-    }
-
-    /**
-     *  returned message
-     *  retMsg.obj = AsyncResult ar
-     *  ar.exception carries exception on failure
-     *  ar.userObject contains the original value of result.obj
-     *  ar.result is String containing IMEI on success
-     */
-    @Override
-    public void getIMEI(Message result) {
-        SimulatedCommandsVerifier.getInstance().getIMEI(result);
-        resultSuccess(result, mImei != null ? mImei : FAKE_IMEI);
-    }
-
-    public void setIMEISV(String imeisv) {
-        mImeiSv = imeisv;
-    }
-
-    /**
-     *  returned message
-     *  retMsg.obj = AsyncResult ar
-     *  ar.exception carries exception on failure
-     *  ar.userObject contains the original value of result.obj
-     *  ar.result is String containing IMEISV on success
-     */
-    @Override
-    public void getIMEISV(Message result) {
-        SimulatedCommandsVerifier.getInstance().getIMEISV(result);
-        resultSuccess(result, mImeiSv != null ? mImeiSv : FAKE_IMEISV);
-    }
-
     /**
      * Hang up one individual connection.
      *  returned message
@@ -900,21 +832,6 @@
         resultSuccess(result, mFailCause);
     }
 
-    /**
-     * @deprecated
-     */
-    @Deprecated
-    @Override
-    public void getLastPdpFailCause (Message result) {
-        unimplemented(result);
-    }
-
-    @Override
-    public void getLastDataCallFailCause(Message result) {
-        //
-        unimplemented(result);
-    }
-
     @Override
     public void setMute (boolean enableMute, Message result) {unimplemented(result);}
 
@@ -1059,10 +976,10 @@
         Object ret = mDataRegStateResult;
         if (ret == null) {
             ret = new DataRegStateResult();
-            ((DataRegStateResult) ret).regState = mDataRegState;
-            ((DataRegStateResult) ret).rat = mDataRadioTech;
-            ((DataRegStateResult) ret).maxDataCalls = mMaxDataCalls;
-            ((DataRegStateResult) ret).reasonDataDenied = mReasonForDenial;
+            ((DataRegStateResult) ret).base.regState = mDataRegState;
+            ((DataRegStateResult) ret).base.rat = mDataRadioTech;
+            ((DataRegStateResult) ret).base.maxDataCalls = mMaxDataCalls;
+            ((DataRegStateResult) ret).base.reasonDataDenied = mReasonForDenial;
         }
 
         resultSuccess(result, ret);
@@ -1212,29 +1129,28 @@
     }
 
     @Override
-    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
-            boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
-            boolean matchAllRuleAllowed, Message result) {
+    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean allowRoaming,
+            int reason, LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo,
+            TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, Message result) {
 
         SimulatedCommandsVerifier.getInstance().setupDataCall(accessNetworkType, dataProfile,
-                isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
-                trafficDescriptor, matchAllRuleAllowed, result);
+                allowRoaming, reason, linkProperties, pduSessionId, sliceInfo, trafficDescriptor,
+                matchAllRuleAllowed, result);
 
         if (mSetupDataCallResult == null) {
             try {
                 mSetupDataCallResult = new SetupDataCallResult();
-                mSetupDataCallResult.status = 0;
+                mSetupDataCallResult.cause = 0;
                 mSetupDataCallResult.suggestedRetryTime = -1;
                 mSetupDataCallResult.cid = 1;
                 mSetupDataCallResult.active = 2;
-                mSetupDataCallResult.type = "IP";
+                mSetupDataCallResult.type = PdpProtocolType.IP;
                 mSetupDataCallResult.ifname = "rmnet_data7";
-                mSetupDataCallResult.addresses = "12.34.56.78";
-                mSetupDataCallResult.dnses = "98.76.54.32";
-                mSetupDataCallResult.gateways = "11.22.33.44";
-                mSetupDataCallResult.pcscf =
-                        "fd00:976a:c305:1d::8 fd00:976a:c202:1d::7 fd00:976a:c305:1d::5";
+                mSetupDataCallResult.addresses = new ArrayList<>(List.of("12.34.56.78"));
+                mSetupDataCallResult.dnses = new ArrayList<>(List.of("98.76.54.32"));
+                mSetupDataCallResult.gateways = new ArrayList<>(List.of("11.22.33.44"));
+                mSetupDataCallResult.pcscf = new ArrayList<>(List.of(
+                        "fd00:976a:c305:1d::8 fd00:976a:c202:1d::7 fd00:976a:c305:1d::5"));
                 mSetupDataCallResult.mtu = 1440;
             } catch (Exception e) {
 
@@ -1624,21 +1540,6 @@
         resultSuccess(response, null);
     }
 
-
-    @Override
-    public void resetRadio(Message result) {
-        unimplemented(result);
-    }
-
-    @Override
-    public void invokeOemRilRequestRaw(byte[] data, Message response) {
-        // Just echo back data
-        if (response != null) {
-            AsyncResult.forMessage(response).result = data;
-            response.sendToTarget();
-        }
-    }
-
     @Override
     public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
                                                 Message response) {
@@ -1649,15 +1550,6 @@
         }
     }
 
-    @Override
-    public void invokeOemRilRequestStrings(String[] strings, Message response) {
-        // Just echo back data
-        if (response != null) {
-            AsyncResult.forMessage(response).result = strings;
-            response.sendToTarget();
-        }
-    }
-
     //***** SimulatedRadioControl
 
 
@@ -2119,14 +2011,14 @@
     }
 
     @Override
-    public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
-        SimulatedCommandsVerifier.getInstance().setInitialAttachApn(dataProfile, isRoaming, result);
+    public void setInitialAttachApn(DataProfile dataProfile, Message result) {
+        SimulatedCommandsVerifier.getInstance().setInitialAttachApn(dataProfile, result);
         resultSuccess(result, null);
     }
 
     @Override
-    public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
-        SimulatedCommandsVerifier.getInstance().setDataProfile(dps, isRoaming, result);
+    public void setDataProfile(DataProfile[] dps, Message result) {
+        SimulatedCommandsVerifier.getInstance().setDataProfile(dps, result);
         resultSuccess(result, null);
     }
 
@@ -2231,22 +2123,6 @@
     }
 
     @Override
-    public void startLceService(int report_interval_ms, boolean pullMode, Message result) {
-        SimulatedCommandsVerifier.getInstance().startLceService(report_interval_ms, pullMode,
-                result);
-    }
-
-    @Override
-    public void stopLceService(Message result) {
-        unimplemented(result);
-    }
-
-    @Override
-    public void pullLceData(Message result) {
-        unimplemented(result);
-    }
-
-    @Override
     public void registerForLceInfo(Handler h, int what, Object obj) {
         SimulatedCommandsVerifier.getInstance().registerForLceInfo(h, what, obj);
     }
@@ -2564,15 +2440,6 @@
                 new ReceivedPhonebookRecords(4, phonebookRecordInfoGroup), null));
     }
 
-    public void setSupportsEid(boolean supportsEid) {
-        mSupportsEid = supportsEid;
-    }
-
-    @Override
-    public boolean supportsEid() {
-        return mSupportsEid;
-    }
-
     @Override
     public void getSimPhonebookCapacity(Message result) {
         resultSuccess(result, new AdnCapacity(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
index 4d1c104..4858e91 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
@@ -688,11 +688,6 @@
     }
 
     @Override
-    public void getPDPContextList(Message result) {
-
-    }
-
-    @Override
     public void getDataCallList(Message result) {
 
     }
@@ -719,16 +714,6 @@
     }
 
     @Override
-    public void getIMEI(Message result) {
-
-    }
-
-    @Override
-    public void getIMEISV(Message result) {
-
-    }
-
-    @Override
     public void hangupConnection(int gsmIndex, Message result) {
 
     }
@@ -789,16 +774,6 @@
     }
 
     @Override
-    public void getLastPdpFailCause(Message result) {
-
-    }
-
-    @Override
-    public void getLastDataCallFailCause(Message result) {
-
-    }
-
-    @Override
     public void setMute(boolean enableMute, Message response) {
 
     }
@@ -1039,11 +1014,6 @@
     }
 
     @Override
-    public void resetRadio(Message result) {
-
-    }
-
-    @Override
     public void setBandMode(int bandMode, Message response) {
 
     }
@@ -1100,26 +1070,6 @@
     }
 
     @Override
-    public void invokeOemRilRequestRaw(byte[] data, Message response) {
-
-    }
-
-    @Override
-    public void invokeOemRilRequestStrings(String[] strings, Message response) {
-
-    }
-
-    @Override
-    public void setOnUnsolOemHookRaw(Handler h, int what, Object obj) {
-
-    }
-
-    @Override
-    public void unSetOnUnsolOemHookRaw(Handler h) {
-
-    }
-
-    @Override
     public void sendTerminalResponse(String contents, Message response) {
 
     }
@@ -1210,10 +1160,9 @@
     }
 
     @Override
-    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
-            boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
-            boolean matchAllRuleAllowed, Message result) {
+    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean allowRoaming,
+            int reason, LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo,
+            TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, Message result) {
     }
 
     @Override
@@ -1247,14 +1196,6 @@
     }
 
     @Override
-    public void getIccSlotsStatus(Message result) {
-    }
-
-    @Override
-    public void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result) {
-    }
-
-    @Override
     public void requestIccSimAuthentication(int authContext, String data, String aid,
                                             Message response) {
 
@@ -1284,12 +1225,12 @@
     }
 
     @Override
-    public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
+    public void setInitialAttachApn(DataProfile dataProfile, Message result) {
 
     }
 
     @Override
-    public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
+    public void setDataProfile(DataProfile[] dps, Message result) {
 
     }
 
@@ -1378,21 +1319,6 @@
     }
 
     @Override
-    public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
-
-    }
-
-    @Override
-    public void stopLceService(Message result) {
-
-    }
-
-    @Override
-    public void pullLceData(Message result) {
-
-    }
-
-    @Override
     public void registerForLceInfo(Handler h, int what, Object obj) {
 
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index 35a3186..95361b3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -108,6 +108,7 @@
     private int mRadioPowerState = RADIO_POWER_UNAVAILABLE;
     private int mDataConnectionState = TelephonyManager.DATA_UNKNOWN;
     private int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
     private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
     private CellLocation mCellLocation;
     private List<CellInfo> mCellInfo;
@@ -185,7 +186,8 @@
             TelephonyCallback.ServiceStateListener,
             TelephonyCallback.CellInfoListener,
             TelephonyCallback.BarringInfoListener,
-            TelephonyCallback.RegistrationFailedListener {
+            TelephonyCallback.RegistrationFailedListener,
+            TelephonyCallback.DataActivityListener {
         // This class isn't mockable to get invocation counts because the IBinder is null and
         // crashes the TelephonyRegistry. Make a cheesy verify(times()) alternative.
         public AtomicInteger invocationCount = new AtomicInteger(0);
@@ -268,6 +270,11 @@
             mCellIdentityForRegiFail = cellIdentity;
             mRegistrationFailReason = causeCode;
         }
+
+        public void onDataActivity(@Annotation.DataActivityType int direction) {
+            invocationCount.incrementAndGet();
+            mDataActivity = direction;
+        }
     }
 
     private void addTelephonyRegistryService() {
@@ -1490,4 +1497,38 @@
         assertEquals(2, mTelephonyCallback.invocationCount.get());
         assertEquals(mCellInfo, dummyCellInfo);
     }
+
+    @Test
+    public void testNotifyDataActivityForSubscriber() {
+        final int subId = 1;
+        int[] events = {TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED};
+        doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
+        doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
+
+        assertEquals(TelephonyManager.DATA_ACTIVITY_NONE, mDataActivity);
+        mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
+                mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+
+        mTelephonyRegistry.notifyDataActivityForSubscriber(0/*phoneId*/, subId,
+                TelephonyManager.DATA_ACTIVITY_INOUT);
+        processAllMessages();
+        assertEquals(TelephonyManager.DATA_ACTIVITY_INOUT, mDataActivity);
+    }
+
+    @Test
+    public void testNotifyDataActivityForSubscriberForInvalidSubId() {
+        final int subId = INVALID_SUBSCRIPTION_ID;
+        int[] events = {TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED};
+        doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
+        doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
+
+        assertEquals(TelephonyManager.DATA_ACTIVITY_NONE, mDataActivity);
+        mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
+                mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+
+        mTelephonyRegistry.notifyDataActivityForSubscriber(0/*phoneId*/, subId,
+                TelephonyManager.DATA_ACTIVITY_OUT);
+        processAllMessages();
+        assertEquals(TelephonyManager.DATA_ACTIVITY_OUT, mDataActivity);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 705bafd..d304f5c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -94,6 +94,7 @@
 import com.android.ims.ImsCall;
 import com.android.ims.ImsEcbm;
 import com.android.ims.ImsManager;
+import com.android.internal.telephony.analytics.TelephonyAnalytics;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriManager;
 import com.android.internal.telephony.data.AccessNetworksManager;
@@ -260,6 +261,7 @@
     protected PersistAtomsStorage mPersistAtomsStorage;
     protected MetricsCollector mMetricsCollector;
     protected SmsStats mSmsStats;
+    protected TelephonyAnalytics mTelephonyAnalytics;
     protected SignalStrength mSignalStrength;
     protected WifiManager mWifiManager;
     protected WifiInfo mWifiInfo;
@@ -497,6 +499,7 @@
         mPersistAtomsStorage = Mockito.mock(PersistAtomsStorage.class);
         mMetricsCollector = Mockito.mock(MetricsCollector.class);
         mSmsStats = Mockito.mock(SmsStats.class);
+        mTelephonyAnalytics = Mockito.mock(TelephonyAnalytics.class);
         mSignalStrength = Mockito.mock(SignalStrength.class);
         mWifiManager = Mockito.mock(WifiManager.class);
         mWifiInfo = Mockito.mock(WifiInfo.class);
@@ -653,6 +656,7 @@
         doReturn(mVoiceCallSessionStats).when(mPhone).getVoiceCallSessionStats();
         doReturn(mVoiceCallSessionStats).when(mImsPhone).getVoiceCallSessionStats();
         doReturn(mSmsStats).when(mPhone).getSmsStats();
+        doReturn(mTelephonyAnalytics).when(mPhone).getTelephonyAnalytics();
         doReturn(mImsStats).when(mImsPhone).getImsStats();
         mIccSmsInterfaceManager.mDispatchersController = mSmsDispatchersController;
         doReturn(mLinkBandwidthEstimator).when(mPhone).getLinkBandwidthEstimator();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/analytics/CallAnalyticsProviderTest.java b/tests/telephonytests/src/com/android/internal/telephony/analytics/CallAnalyticsProviderTest.java
new file mode 100644
index 0000000..076ee06
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/analytics/CallAnalyticsProviderTest.java
@@ -0,0 +1,443 @@
+/*
+ * 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.internal.telephony.analytics;
+
+import static android.os.Build.VERSION.INCREMENTAL;
+
+import static com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.CallAnalyticsTable;
+import static com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.DATE_FORMAT;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Calendar;
+
+public class CallAnalyticsProviderTest {
+
+    @Mock TelephonyAnalyticsUtil mTelephonyAnalyticsUtil;
+    @Mock Cursor mCursor;
+    private CallAnalyticsProvider mCallAnalyticsProvider;
+    private ContentValues mContentValues;
+
+    enum CallStatus {
+        SUCCESS("Success"),
+        FAILURE("Failure");
+        public String value;
+
+        CallStatus(String value) {
+            this.value = value;
+        }
+    }
+
+    enum CallType {
+        NORMAL("Normal Call"),
+        SOS("SOS Call");
+        public String value;
+
+        CallType(String value) {
+            this.value = value;
+        }
+    }
+
+    final String[] mCallInsertionProjection = {
+        TelephonyAnalyticsDatabase.CallAnalyticsTable._ID,
+        TelephonyAnalyticsDatabase.CallAnalyticsTable.COUNT
+    };
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        final String createCallAnalyticsTable =
+                "CREATE TABLE IF NOT EXISTS "
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.TABLE_NAME
+                        + "("
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable._ID
+                        + " INTEGER PRIMARY KEY,"
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.LOG_DATE
+                        + " DATE ,"
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.CALL_STATUS
+                        + " TEXT DEFAULT '',"
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.CALL_TYPE
+                        + " TEXT DEFAULT '',"
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.RAT
+                        + " TEXT DEFAULT '',"
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.SLOT_ID
+                        + " INTEGER ,"
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.FAILURE_REASON
+                        + " TEXT DEFAULT '',"
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.RELEASE_VERSION
+                        + " TEXT DEFAULT '' , "
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.COUNT
+                        + " INTEGER DEFAULT 1 "
+                        + ");";
+        mCallAnalyticsProvider = new CallAnalyticsProvider(mTelephonyAnalyticsUtil, 0);
+        verify(mTelephonyAnalyticsUtil).createTable(createCallAnalyticsTable);
+    }
+
+    @Test
+    public void testAggregate() {
+        String[] columns = {"sum(" + CallAnalyticsTable.COUNT + ")"};
+
+        when(mTelephonyAnalyticsUtil.getCursor(
+                        eq(CallAnalyticsTable.TABLE_NAME),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull()))
+                .thenReturn(mCursor);
+        when(mTelephonyAnalyticsUtil.getCursor(
+                        anyString(),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        anyString(),
+                        isNull(),
+                        anyString(),
+                        anyString()))
+                .thenReturn(mCursor);
+
+        when(mTelephonyAnalyticsUtil.getCountFromCursor(eq(mCursor)))
+                .thenReturn(
+                        100L /*totalCalls*/,
+                        50L /*totalFailedCalls*/,
+                        40L /*normalCalls*/,
+                        10L /*failedNormalCall*/,
+                        60L /*sosCalls*/,
+                        40L /*failedSosCall*/);
+        ArrayList<String> actual = mCallAnalyticsProvider.aggregate();
+        verify(mTelephonyAnalyticsUtil, times(6))
+                .getCursor(
+                        eq(CallAnalyticsTable.TABLE_NAME),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull());
+        assertEquals("\tTotal Normal Calls = " + 40 /*normalCalls*/, actual.get(1));
+        assertEquals("\tPercentage Failure of Normal Calls = 25.00%", actual.get(2));
+    }
+
+    @Test
+    public void testGetMaxFailureVersion() {
+        String[] columns = {CallAnalyticsTable.RELEASE_VERSION};
+        String selection =
+                CallAnalyticsTable.CALL_STATUS + " = ? AND " + CallAnalyticsTable.SLOT_ID + " = ? ";
+        String[] selectionArgs = {"Failure", Integer.toString(0 /* slotIndex */)};
+        String groupBy = CallAnalyticsTable.RELEASE_VERSION;
+        String orderBy = "SUM(" + CallAnalyticsTable.COUNT + ") DESC ";
+        String limit = "1";
+        when(mTelephonyAnalyticsUtil.getCursor(
+                        anyString(),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        anyString(),
+                        isNull(),
+                        anyString(),
+                        anyString()))
+                .thenReturn(mCursor);
+        when(mTelephonyAnalyticsUtil.getCountFromCursor(any(Cursor.class)))
+                .thenReturn(10L /* count */);
+        when(mTelephonyAnalyticsUtil.getCountFromCursor(isNull())).thenReturn(10L /* count */);
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex(CallAnalyticsTable.RELEASE_VERSION))
+                .thenReturn(0 /* releaseVersionColumnIndex */);
+        when(mCursor.getString(0)).thenReturn("1.1.1.1" /* version */);
+        ArrayList<String> actual = mCallAnalyticsProvider.aggregate();
+        verify(mTelephonyAnalyticsUtil)
+                .getCursor(
+                        eq(CallAnalyticsTable.TABLE_NAME),
+                        eq(columns),
+                        eq(selection),
+                        eq(selectionArgs),
+                        eq(groupBy),
+                        isNull(),
+                        eq(orderBy),
+                        eq(limit));
+        assertEquals(
+                actual.get(actual.size() - 2 /* array index for max failure at version info */),
+                "\tMax Call(Normal+SOS) Failures at Version : 1.1.1.1");
+    }
+
+    private ContentValues getContentValues(
+            String callType, String callStatus, int slotId, String rat, String failureReason) {
+        ContentValues values = new ContentValues();
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        values.put(CallAnalyticsTable.LOG_DATE, dateToday);
+        values.put(CallAnalyticsTable.CALL_TYPE, callType);
+        values.put(CallAnalyticsTable.CALL_STATUS, callStatus);
+        values.put(CallAnalyticsTable.SLOT_ID, slotId);
+        values.put(CallAnalyticsTable.RAT, rat);
+        values.put(CallAnalyticsTable.FAILURE_REASON, failureReason);
+        values.put(CallAnalyticsTable.RELEASE_VERSION, INCREMENTAL);
+        return values;
+    }
+
+    private void whenConditionForGetCursor() {
+        when(mTelephonyAnalyticsUtil.getCursor(
+                        anyString(),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull()))
+                .thenReturn(mCursor);
+    }
+
+    private void verifyForGetCursor(
+            String[] callInsertionProjection,
+            String callSuccessInsertionSelection,
+            String[] selectionArgs) {
+
+        verify(mTelephonyAnalyticsUtil)
+                .getCursor(
+                        eq(TelephonyAnalyticsDatabase.CallAnalyticsTable.TABLE_NAME),
+                        eq(callInsertionProjection),
+                        eq(callSuccessInsertionSelection),
+                        eq(selectionArgs),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull());
+    }
+
+    @Test
+    public void testSuccessCall() {
+        int slotId = 0;
+        String callType = "Normal Call";
+        String callStatus = "Success";
+        String rat = "LTE";
+        String failureReason = "User Disconnects";
+        int count = 5;
+
+        final String callSuccessInsertionSelection =
+                TelephonyAnalyticsDatabase.CallAnalyticsTable.CALL_TYPE
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.LOG_DATE
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.CALL_STATUS
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.CallAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        ContentValues values = getContentValues(callType, callStatus, slotId, rat, failureReason);
+
+        String[] selectionArgs =
+                new String[] {
+                    values.getAsString(TelephonyAnalyticsDatabase.CallAnalyticsTable.CALL_TYPE),
+                    values.getAsString(TelephonyAnalyticsDatabase.CallAnalyticsTable.LOG_DATE),
+                    callStatus,
+                    values.getAsString(TelephonyAnalyticsDatabase.CallAnalyticsTable.SLOT_ID)
+                };
+        whenConditionForGetCursor();
+        mCallAnalyticsProvider.insertDataToDb(callType, callStatus, slotId, rat, failureReason);
+        verifyForGetCursor(mCallInsertionProjection, callSuccessInsertionSelection, selectionArgs);
+    }
+
+    @Test
+    public void testFailureCall() {
+        int slotId = 0;
+        String callType = "Normal Call";
+        String callStatus = "Failure";
+        String rat = "LTE";
+        String failureReason = "Network Detach";
+        int count = 5;
+        final String callFailedInsertionSelection =
+                CallAnalyticsTable.LOG_DATE
+                        + " = ? AND "
+                        + CallAnalyticsTable.CALL_STATUS
+                        + " = ? AND "
+                        + CallAnalyticsTable.CALL_TYPE
+                        + " = ? AND "
+                        + CallAnalyticsTable.SLOT_ID
+                        + " = ? AND "
+                        + CallAnalyticsTable.RAT
+                        + " = ? AND "
+                        + CallAnalyticsTable.FAILURE_REASON
+                        + " = ? AND "
+                        + CallAnalyticsTable.RELEASE_VERSION
+                        + " = ? ";
+        ContentValues values = getContentValues(callType, callStatus, slotId, rat, failureReason);
+        String[] selectionArgs = {
+            values.getAsString(CallAnalyticsTable.LOG_DATE),
+            values.getAsString(CallAnalyticsTable.CALL_STATUS),
+            values.getAsString(CallAnalyticsTable.CALL_TYPE),
+            values.getAsString(CallAnalyticsTable.SLOT_ID),
+            values.getAsString(CallAnalyticsTable.RAT),
+            values.getAsString(CallAnalyticsTable.FAILURE_REASON),
+            values.getAsString(CallAnalyticsTable.RELEASE_VERSION)
+        };
+        whenConditionForGetCursor();
+        mCallAnalyticsProvider.insertDataToDb(callType, callStatus, slotId, rat, failureReason);
+        verifyForGetCursor(mCallInsertionProjection, callFailedInsertionSelection, selectionArgs);
+    }
+
+    public void setUpTestForUpdateEntryIfExistsOrInsert() throws NoSuchMethodException {
+        Method updateEntryIfExistsOrInsert =
+                CallAnalyticsProvider.class.getDeclaredMethod(
+                        "updateEntryIfExistsOrInsert", Cursor.class, ContentValues.class);
+        updateEntryIfExistsOrInsert.setAccessible(true);
+        mContentValues = new ContentValues();
+        mContentValues.put(CallAnalyticsTable.CALL_STATUS, "Success");
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenCursorNull()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert =
+                CallAnalyticsProvider.class.getDeclaredMethod(
+                        "updateEntryIfExistsOrInsert", Cursor.class, ContentValues.class);
+        updateEntryIfExistsOrInsert.setAccessible(true);
+        ContentValues values = new ContentValues();
+        values.put(CallAnalyticsTable.CALL_STATUS, "Success");
+        updateEntryIfExistsOrInsert.invoke(mCallAnalyticsProvider, null, values);
+        verify(mTelephonyAnalyticsUtil).insert(eq(CallAnalyticsTable.TABLE_NAME), eq(values));
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenCursorInvalid()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert =
+                CallAnalyticsProvider.class.getDeclaredMethod(
+                        "updateEntryIfExistsOrInsert", Cursor.class, ContentValues.class);
+        updateEntryIfExistsOrInsert.setAccessible(true);
+        ContentValues values = new ContentValues();
+        values.put(CallAnalyticsTable.CALL_STATUS, "Success");
+        when(mCursor.moveToFirst()).thenReturn(false);
+        updateEntryIfExistsOrInsert.invoke(mCallAnalyticsProvider, mCursor, values);
+        verify(mTelephonyAnalyticsUtil).insert(eq(CallAnalyticsTable.TABLE_NAME), eq(values));
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenCursorValidUpdateSuccess()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert =
+                CallAnalyticsProvider.class.getDeclaredMethod(
+                        "updateEntryIfExistsOrInsert", Cursor.class, ContentValues.class);
+        updateEntryIfExistsOrInsert.setAccessible(true);
+
+        ContentValues values = new ContentValues();
+        values.put(CallAnalyticsTable.CALL_STATUS, "Success");
+
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex(CallAnalyticsTable._ID)).thenReturn(0 /* idColumnIndex */);
+        when(mCursor.getColumnIndex(CallAnalyticsTable.COUNT)).thenReturn(1 /* countColumnIndex */);
+        when(mCursor.getInt(0 /* idColumnIndex */)).thenReturn(100 /* id */);
+        when(mCursor.getInt(1 /* countColumnIndex */)).thenReturn(5 /* count*/);
+
+        String updateSelection = CallAnalyticsTable._ID + " = ? ";
+        String[] updateSelectionArgs = {String.valueOf(100 /* id */)};
+
+        updateEntryIfExistsOrInsert.invoke(mCallAnalyticsProvider, mCursor, values);
+
+        values.put(CallAnalyticsTable.COUNT, 6 /* newCount */);
+        verify(mTelephonyAnalyticsUtil)
+                .update(
+                        eq(CallAnalyticsTable.TABLE_NAME),
+                        eq(values),
+                        eq(updateSelection),
+                        eq(updateSelectionArgs));
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenUpdateFailedDueToInvalidIdIndex()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert =
+                CallAnalyticsProvider.class.getDeclaredMethod(
+                        "updateEntryIfExistsOrInsert", Cursor.class, ContentValues.class);
+        updateEntryIfExistsOrInsert.setAccessible(true);
+
+        ContentValues values = new ContentValues();
+        values.put(CallAnalyticsTable.CALL_STATUS, "Success");
+
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex(CallAnalyticsTable._ID)).thenReturn(-1 /* idColumnIndex */);
+        when(mCursor.getColumnIndex(CallAnalyticsTable.COUNT)).thenReturn(1 /* countColumnIndex */);
+        updateEntryIfExistsOrInsert.invoke(mCallAnalyticsProvider, mCursor, values);
+        verifyNoMoreInteractions(mTelephonyAnalyticsUtil);
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenUpdateFailedDueToInvalidCountIndex()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert =
+                CallAnalyticsProvider.class.getDeclaredMethod(
+                        "updateEntryIfExistsOrInsert", Cursor.class, ContentValues.class);
+        updateEntryIfExistsOrInsert.setAccessible(true);
+
+        ContentValues values = new ContentValues();
+        values.put(CallAnalyticsTable.CALL_STATUS, "Success");
+
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex(CallAnalyticsTable._ID)).thenReturn(0 /* idColumnIndex */);
+        when(mCursor.getColumnIndex(CallAnalyticsTable.COUNT))
+                .thenReturn(-1 /* countColumnIndex */);
+        updateEntryIfExistsOrInsert.invoke(mCallAnalyticsProvider, mCursor, values);
+        verifyNoMoreInteractions(mTelephonyAnalyticsUtil);
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenUpdateFailedDueToInvalidColumnIndex()
+            throws NoSuchMethodException {
+        Method updateEntryIfExistsOrInsert =
+                CallAnalyticsProvider.class.getDeclaredMethod(
+                        "updateEntryIfExistsOrInsert", Cursor.class, ContentValues.class);
+        updateEntryIfExistsOrInsert.setAccessible(true);
+
+        ContentValues values = new ContentValues();
+        values.put(CallAnalyticsTable.CALL_STATUS, "Success");
+
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex(CallAnalyticsTable._ID)).thenReturn(-1 /* idColumnIndex */);
+        when(mCursor.getColumnIndex(CallAnalyticsTable.COUNT))
+                .thenReturn(-1 /* countColumnIndex */);
+        verifyNoMoreInteractions(mTelephonyAnalyticsUtil);
+    }
+
+    @After
+    public void tearDown() {
+        mCallAnalyticsProvider = null;
+        mContentValues = null;
+        mTelephonyAnalyticsUtil = null;
+        mCursor = null;
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/analytics/ServiceStateAnalyticsProviderTest.java b/tests/telephonytests/src/com/android/internal/telephony/analytics/ServiceStateAnalyticsProviderTest.java
new file mode 100644
index 0000000..dcc1c73
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/analytics/ServiceStateAnalyticsProviderTest.java
@@ -0,0 +1,645 @@
+/*
+ * 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.internal.telephony.analytics;
+
+import static android.os.Build.VERSION.INCREMENTAL;
+
+import static com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.DATE_FORMAT;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.ServiceStateAnalyticsTable;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashMap;
+
+public class ServiceStateAnalyticsProviderTest {
+    @Mock TelephonyAnalyticsUtil mTelephonyAnalyticsUtil;
+    @Mock Cursor mCursor;
+    @Mock TelephonyAnalytics.ServiceStateAnalytics.TimeStampedServiceState mState;
+    private ContentValues mContentValues;
+
+    final int mSlotIndex = 0;
+    ServiceStateAnalyticsProvider mServiceStateAnalyticsProvider;
+    final String mCreateServiceStateTableQuery =
+            "CREATE TABLE IF NOT EXISTS "
+                    + ServiceStateAnalyticsTable.TABLE_NAME
+                    + " ( "
+                    + ServiceStateAnalyticsTable._ID
+                    + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+                    + ServiceStateAnalyticsTable.LOG_DATE
+                    + " DATE ,"
+                    + ServiceStateAnalyticsTable.SLOT_ID
+                    + " INTEGER , "
+                    + ServiceStateAnalyticsTable.TIME_DURATION
+                    + " INTEGER ,"
+                    + ServiceStateAnalyticsTable.RAT
+                    + " TEXT ,"
+                    + ServiceStateAnalyticsTable.DEVICE_STATUS
+                    + " TEXT ,"
+                    + ServiceStateAnalyticsTable.RELEASE_VERSION
+                    + " TEXT "
+                    + ");";
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        assert (mTelephonyAnalyticsUtil != null);
+        mServiceStateAnalyticsProvider =
+                new ServiceStateAnalyticsProvider(mTelephonyAnalyticsUtil, mSlotIndex);
+        verify(mTelephonyAnalyticsUtil).createTable(mCreateServiceStateTableQuery);
+        mContentValues = getDummyContentValue();
+    }
+
+    @Test
+    public void valid() {
+        assert (mServiceStateAnalyticsProvider != null);
+        assert (mTelephonyAnalyticsUtil != null);
+        assert (mCursor != null);
+        assert (mState != null);
+    }
+
+    @Test
+    public void testInsertDataToDb() {
+        TelephonyAnalytics.ServiceStateAnalytics.TimeStampedServiceState lastState =
+                new TelephonyAnalytics.ServiceStateAnalytics.TimeStampedServiceState(
+                        0 /*slotIndex*/,
+                        "LTE" /*rat*/,
+                        "IN_SERVICE" /*deviceStatus*/,
+                        233423424 /*timestampStart*/);
+        ContentValues values = new ContentValues();
+        long timeInterval = 343443434 /*endTimeStamp*/ - lastState.getTimestampStart();
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        values.put(ServiceStateAnalyticsTable.LOG_DATE, dateToday);
+        values.put(ServiceStateAnalyticsTable.TIME_DURATION, timeInterval);
+        values.put(ServiceStateAnalyticsTable.SLOT_ID, lastState.getSlotIndex());
+        values.put(ServiceStateAnalyticsTable.RAT, lastState.getRAT());
+        values.put(ServiceStateAnalyticsTable.DEVICE_STATUS, lastState.getDeviceStatus());
+        values.put(ServiceStateAnalyticsTable.RELEASE_VERSION, INCREMENTAL);
+
+        final String[] serviceStateInsertionColumns = {
+            ServiceStateAnalyticsTable._ID, ServiceStateAnalyticsTable.TIME_DURATION
+        };
+        final String serviceStateInsertionSelection =
+                ServiceStateAnalyticsTable.LOG_DATE
+                        + " = ? AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? AND "
+                        + ServiceStateAnalyticsTable.RAT
+                        + " = ? AND "
+                        + ServiceStateAnalyticsTable.DEVICE_STATUS
+                        + " = ? AND "
+                        + ServiceStateAnalyticsTable.RELEASE_VERSION
+                        + " = ? ";
+        final String[] selectionArgs = {
+            values.getAsString(ServiceStateAnalyticsTable.LOG_DATE),
+            values.getAsString(ServiceStateAnalyticsTable.SLOT_ID),
+            values.getAsString(ServiceStateAnalyticsTable.RAT),
+            values.getAsString(ServiceStateAnalyticsTable.DEVICE_STATUS),
+            values.getAsString(ServiceStateAnalyticsTable.RELEASE_VERSION)
+        };
+        when(mTelephonyAnalyticsUtil.getCursor(
+                        anyString(),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull()))
+                .thenReturn(mCursor);
+        mServiceStateAnalyticsProvider.insertDataToDb(lastState, 343443434 /*endTimeStamp*/);
+
+        verify(mTelephonyAnalyticsUtil)
+                .getCursor(
+                        eq(ServiceStateAnalyticsTable.TABLE_NAME),
+                        eq(serviceStateInsertionColumns),
+                        eq(serviceStateInsertionSelection),
+                        eq(selectionArgs),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull());
+    }
+
+    private Method setReflectionForTestingUpdateEntryIfExistsOrInsert()
+            throws NoSuchMethodException {
+        Method updateEntryIfExistsOrInsert =
+                ServiceStateAnalyticsProvider.class.getDeclaredMethod(
+                        "updateIfEntryExistsOtherwiseInsert", Cursor.class, ContentValues.class);
+        updateEntryIfExistsOrInsert.setAccessible(true);
+        return updateEntryIfExistsOrInsert;
+    }
+
+    private ContentValues getDummyContentValue() {
+        ContentValues values = new ContentValues();
+        values.put(ServiceStateAnalyticsTable.DEVICE_STATUS, "IN_SERVICE" /*DeviceStatus*/);
+        values.put(ServiceStateAnalyticsTable.TIME_DURATION, 423234 /*Time Duration*/);
+        return values;
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenCursorNull()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert = setReflectionForTestingUpdateEntryIfExistsOrInsert();
+        updateEntryIfExistsOrInsert.invoke(mServiceStateAnalyticsProvider, null, mContentValues);
+        verify(mTelephonyAnalyticsUtil)
+                .insert(eq(ServiceStateAnalyticsTable.TABLE_NAME), eq(mContentValues));
+    }
+
+    @Test
+    public void testUpdateIfEntryExistsOtherwiseInsertWhenEntryNotExist()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert = setReflectionForTestingUpdateEntryIfExistsOrInsert();
+        when(mCursor.moveToFirst()).thenReturn(false);
+        updateEntryIfExistsOrInsert.invoke(mServiceStateAnalyticsProvider, null, mContentValues);
+        verify(mTelephonyAnalyticsUtil)
+                .insert(eq(ServiceStateAnalyticsTable.TABLE_NAME), eq(mContentValues));
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenCursorValidAndUpdateSuccess()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert = setReflectionForTestingUpdateEntryIfExistsOrInsert();
+
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable._ID)).thenReturn(0 /* idIndex */);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.TIME_DURATION))
+                .thenReturn(1 /* timeDurationIndex */);
+        when(mCursor.getInt(0 /* idIndex */)).thenReturn(100 /* ID */);
+        when(mCursor.getInt(1 /* timeDurationIndex */)).thenReturn(523535 /* oldTimeDuration */);
+
+        String updateSelection = ServiceStateAnalyticsTable._ID + " = ? ";
+        String[] updateSelectionArgs = {Integer.toString(100 /* ID */)};
+
+        updateEntryIfExistsOrInsert.invoke(mServiceStateAnalyticsProvider, mCursor, mContentValues);
+        mContentValues.put(
+                ServiceStateAnalyticsTable.TIME_DURATION, 946769 /* updatedTimeDuration */);
+        verify(mTelephonyAnalyticsUtil)
+                .update(
+                        eq(ServiceStateAnalyticsTable.TABLE_NAME),
+                        eq(mContentValues),
+                        eq(updateSelection),
+                        eq(updateSelectionArgs));
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenUpdateFailedDueToInvalidIdIndex()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert = setReflectionForTestingUpdateEntryIfExistsOrInsert();
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable._ID)).thenReturn(-1 /* idIndex */);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.TIME_DURATION))
+                .thenReturn(1 /* timeDurationIndex */);
+        updateEntryIfExistsOrInsert.invoke(mServiceStateAnalyticsProvider, mCursor, mContentValues);
+        verifyNoMoreInteractions(mTelephonyAnalyticsUtil);
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenUpdateFailedDueToInvalidDurationIndex()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert = setReflectionForTestingUpdateEntryIfExistsOrInsert();
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable._ID)).thenReturn(0 /* idIndex */);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.TIME_DURATION))
+                .thenReturn(-1 /* timeDurationIndex */);
+        updateEntryIfExistsOrInsert.invoke(mServiceStateAnalyticsProvider, mCursor, mContentValues);
+        verifyNoMoreInteractions(mTelephonyAnalyticsUtil);
+    }
+
+    @Test
+    public void testUpdateEntryIfExistsOrInsertWhenUpdateFailedDueToAllInvalidIndex()
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Method updateEntryIfExistsOrInsert = setReflectionForTestingUpdateEntryIfExistsOrInsert();
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable._ID)).thenReturn(-1 /* idIndex */);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.TIME_DURATION))
+                .thenReturn(-1 /* timeDurationIndex */);
+        updateEntryIfExistsOrInsert.invoke(mServiceStateAnalyticsProvider, mCursor, mContentValues);
+        verifyNoMoreInteractions(mTelephonyAnalyticsUtil);
+    }
+
+    private void setWhenClauseForGetCursor(
+            String[] columns, String selection, String[] selectionArgs) {
+        when(mTelephonyAnalyticsUtil.getCursor(
+                        eq(ServiceStateAnalyticsTable.TABLE_NAME),
+                        eq(columns),
+                        eq(selection),
+                        eq(selectionArgs),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull()))
+                .thenReturn(mCursor);
+    }
+
+    private void verifyClause(String[] columns, String selection, String[] selectionArgs) {
+        verify(mTelephonyAnalyticsUtil)
+                .getCursor(
+                        eq(ServiceStateAnalyticsTable.TABLE_NAME),
+                        eq(columns),
+                        eq(selection),
+                        eq(selectionArgs),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull());
+    }
+
+    private void setUpNullCursorReturn() {
+        when(mTelephonyAnalyticsUtil.getCursor(
+                        anyString(),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull()))
+                .thenReturn(null);
+    }
+
+    @Test
+    public void testTotalUpTimeThroughAggregate() {
+        HashMap<String, Long> empty = new HashMap<>();
+        String[] columns = {"SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ")"};
+        String selection = ServiceStateAnalyticsTable.SLOT_ID + " = ? ";
+        String[] selectionArgs = {Integer.toString(mSlotIndex)};
+        setWhenClauseForGetCursor(columns, selection, selectionArgs);
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getLong(0 /* columnIndex */)).thenReturn(1000000L /* upTime */);
+
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        verifyClause(columns, selection, selectionArgs);
+        assertEquals(
+                actual.get(0 /* Array Index for Total Uptime*/),
+                "Total UpTime = " + 1000000 /* upTime */ + " millis");
+    }
+
+    @Test
+    public void testTotalUpTimeWhenCursorNullThroughAggregate() {
+        String[] columns = {"SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ")"};
+        String selection = ServiceStateAnalyticsTable.SLOT_ID + " = ? ";
+        String[] selectionArgs = {Integer.toString(mSlotIndex)};
+        setUpNullCursorReturn();
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        verifyClause(columns, selection, selectionArgs);
+        assertEquals(
+                actual.get(0 /* Array Index for Total Uptime*/),
+                "Total UpTime = " + 0 /*upTime */ + " millis");
+    }
+
+    @Test
+    public void testTotalUpTimeWhenCursorInvalidThroughAggregate() {
+        String[] columns = {"SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ")"};
+        String selection = ServiceStateAnalyticsTable.SLOT_ID + " = ? ";
+        String[] selectionArgs = {Integer.toString(mSlotIndex)};
+        setWhenClauseForGetCursor(columns, selection, selectionArgs);
+        when(mCursor.moveToFirst()).thenReturn(false);
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        verifyClause(columns, selection, selectionArgs);
+        assertEquals(
+                actual.get(0 /* Array Index for Total Uptime*/),
+                "Total UpTime = " + 0 /*upTime */ + " millis");
+    }
+
+    @Test
+    public void testOutOfServiceDurationThroughAggregate() {
+        setUpTotalTime();
+        String[] columns = {"SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ")"};
+        String selection =
+                ServiceStateAnalyticsTable.DEVICE_STATUS
+                        + " != ? "
+                        + " AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"IN_SERVICE", Integer.toString(mSlotIndex)};
+
+        when(mCursor.moveToFirst()).thenReturn(true, true);
+        when(mCursor.getLong(0 /*columnIndex*/))
+                .thenReturn(1000000L /*totalUpTime*/, 100000L /*outOfServiceTime*/);
+        setWhenClauseForGetCursor(columns, selection, selectionArgs);
+
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        verifyClause(columns, selection, selectionArgs);
+        assertEquals(
+                actual.get(1),
+                "Out of Service Time = "
+                        + 100000 /*outOfServiceTime*/
+                        + " millis, Percentage "
+                        + "10.00"
+                        + "%");
+    }
+
+    @Test
+    public void testOutOfServiceDurationWhenCursorNullThroughAggregate() {
+        HashMap<String, Long> empty = new HashMap<>();
+        String[] columns = {"SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ")"};
+        String selection =
+                ServiceStateAnalyticsTable.DEVICE_STATUS
+                        + " != ? "
+                        + " AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"IN_SERVICE", Integer.toString(mSlotIndex)};
+        setUpNullCursorReturn();
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        verifyClause(columns, selection, selectionArgs);
+        boolean check =
+                actual.get(1 /* ArrayIndex for Out Of Service Time Info */)
+                        .contains("Out of Service Time = 0");
+        assertTrue(check);
+    }
+
+    @Test
+    public void testOutOfServiceDurationWhenCursorInvalidThroughAggregate() {
+        HashMap<String, Long> empty = new HashMap<>();
+        String[] columns = {"SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ")"};
+        String selection =
+                ServiceStateAnalyticsTable.DEVICE_STATUS
+                        + " != ? "
+                        + " AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"IN_SERVICE", Integer.toString(mSlotIndex)};
+        setWhenClauseForGetCursor(columns, selection, selectionArgs);
+        when(mCursor.moveToFirst()).thenReturn(false);
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        verifyClause(columns, selection, selectionArgs);
+        boolean check = actual.get(1).contains("Out of Service Time = 0");
+        assertTrue(check);
+    }
+
+    private void whenClauseForGroupByPresent(
+            String[] columns, String selection, String[] selectionArgs, String groupBy) {
+        when(mTelephonyAnalyticsUtil.getCursor(
+                        eq(ServiceStateAnalyticsTable.TABLE_NAME),
+                        eq(columns),
+                        eq(selection),
+                        eq(selectionArgs),
+                        eq(groupBy),
+                        isNull(),
+                        isNull(),
+                        isNull()))
+                .thenReturn(mCursor);
+    }
+
+    private void verifyGroupBy(
+            String[] columns, String selection, String[] selectionArgs, String groupBy) {
+        verify(mTelephonyAnalyticsUtil)
+                .getCursor(
+                        eq(ServiceStateAnalyticsTable.TABLE_NAME),
+                        eq(columns),
+                        eq(selection),
+                        eq(selectionArgs),
+                        eq(groupBy),
+                        isNull(),
+                        isNull(),
+                        isNull());
+    }
+
+    private void setUpTotalTime() {
+        String[] columns = {"SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ")"};
+        String selection = ServiceStateAnalyticsTable.SLOT_ID + " = ? ";
+        String[] selectionArgs = {Integer.toString(mSlotIndex)};
+        setWhenClauseForGetCursor(columns, selection, selectionArgs);
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getLong(0 /*columnIndex*/)).thenReturn(1000000L /*upTime*/);
+    }
+
+    @Test
+    public void testOutOfServiceDurationByReasonWhenValid() {
+        setUpTotalTime();
+        String[] columns = {
+            ServiceStateAnalyticsTable.DEVICE_STATUS,
+            "SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ") AS totalTime"
+        };
+        String selection =
+                ServiceStateAnalyticsTable.DEVICE_STATUS
+                        + " != ? AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"IN_SERVICE", Integer.toString(mSlotIndex)};
+        String groupBy = ServiceStateAnalyticsTable.DEVICE_STATUS;
+        whenClauseForGroupByPresent(columns, selection, selectionArgs, groupBy);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.DEVICE_STATUS))
+                .thenReturn(0 /*deviceStatusIndex*/);
+        when(mCursor.getColumnIndex("totalTime")).thenReturn(1 /*totalTimeIndex*/);
+        when(mCursor.moveToNext()).thenReturn(true, false);
+        when(mCursor.getString(0 /*deviceStatusIndex*/)).thenReturn("NO_NETWORK" /*oosReason*/);
+        when(mCursor.getLong(1 /*totalTimeIndex*/)).thenReturn(100000L /*oosTime*/);
+
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        verifyGroupBy(columns, selection, selectionArgs, groupBy);
+        assertEquals(
+                actual.get(2 /*oosReasonDumpArrayIndex*/),
+                "Out of service Reason = " + "NO_NETWORK" + ", Percentage = " + "10.00" + "%");
+    }
+
+    @Test
+    public void testOutOfServiceDurationByReasonWhenNoDataInCursor() {
+        String[] columns = {
+            ServiceStateAnalyticsTable.DEVICE_STATUS,
+            "SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ") AS totalTime"
+        };
+        String selection =
+                ServiceStateAnalyticsTable.DEVICE_STATUS
+                        + " != ? AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"IN_SERVICE", Integer.toString(mSlotIndex)};
+        String groupBy = ServiceStateAnalyticsTable.DEVICE_STATUS;
+        HashMap<String, Long> outOfServiceDurationByReason = new HashMap<>();
+        outOfServiceDurationByReason.put("NO_NETWORK", 10000L);
+
+        whenClauseForGroupByPresent(columns, selection, selectionArgs, groupBy);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.DEVICE_STATUS))
+                .thenReturn(0 /*deviceStatusIndex*/);
+        when(mCursor.getColumnIndex("totalTime")).thenReturn(1 /*totalTimeIndex*/);
+        when(mCursor.moveToNext()).thenReturn(false);
+
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        verifyGroupBy(columns, selection, selectionArgs, groupBy);
+        assertEquals(actual.size(), 2 /*expectedArraySize*/);
+    }
+
+    @Test
+    public void testOutOfServiceDurationByReasonWhenReasonIndexInvalid() {
+        String[] columns = {
+            ServiceStateAnalyticsTable.DEVICE_STATUS,
+            "SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ") AS totalTime"
+        };
+        String selection =
+                ServiceStateAnalyticsTable.DEVICE_STATUS
+                        + " != ? AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"IN_SERVICE", Integer.toString(mSlotIndex)};
+        String groupBy = ServiceStateAnalyticsTable.DEVICE_STATUS;
+        HashMap<String, Long> outOfServiceDurationByReason = new HashMap<>();
+        outOfServiceDurationByReason.put("NO_NETWORK", 10000L);
+
+        whenClauseForGroupByPresent(columns, selection, selectionArgs, groupBy);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.DEVICE_STATUS))
+                .thenReturn(-1 /*deviceStatusIndex*/);
+        when(mCursor.getColumnIndex("totalTime")).thenReturn(1 /*totalTimeIndex*/);
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        verifyGroupBy(columns, selection, selectionArgs, groupBy);
+        assertEquals(actual.size(), 2 /*expectedArraySize*/);
+    }
+
+    @Test
+    public void testOutOfServiceDurationByReasonWhenTimeIndexInvalid() {
+        String[] columns = {
+            ServiceStateAnalyticsTable.DEVICE_STATUS,
+            "SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ") AS totalTime"
+        };
+        String selection =
+                ServiceStateAnalyticsTable.DEVICE_STATUS
+                        + " != ? AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"IN_SERVICE", Integer.toString(mSlotIndex)};
+        String groupBy = ServiceStateAnalyticsTable.DEVICE_STATUS;
+        HashMap<String, Long> outOfServiceDurationByReason = new HashMap<>();
+        outOfServiceDurationByReason.put("NO_NETWORK", 10000L);
+
+        whenClauseForGroupByPresent(columns, selection, selectionArgs, groupBy);
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.DEVICE_STATUS))
+                .thenReturn(0 /*deviceStatusIndex*/);
+        when(mCursor.getColumnIndex("totalTime")).thenReturn(-1 /*totalTimeIndex*/);
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        verifyGroupBy(columns, selection, selectionArgs, groupBy);
+        assertEquals(actual.size(), 2 /*expectedArraySize*/);
+    }
+
+    private void setUpForTestingGetInServiceDurationByRat() {
+        String[] columns = {
+            ServiceStateAnalyticsTable.RAT,
+            "SUM(" + ServiceStateAnalyticsTable.TIME_DURATION + ") AS totalTime"
+        };
+        String selection =
+                ServiceStateAnalyticsTable.RAT
+                        + " != ? AND "
+                        + ServiceStateAnalyticsTable.SLOT_ID
+                        + " = ? ";
+        String[] selectionArgs = {"NA", Integer.toString(mSlotIndex)};
+        String groupBy = ServiceStateAnalyticsTable.RAT;
+        whenClauseForGroupByPresent(columns, selection, selectionArgs, groupBy);
+    }
+
+    @Test
+    public void testInServiceDurationByRatWhenDataPresent() {
+        setUpForTestingGetInServiceDurationByRat();
+        setUpTotalTime();
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.RAT)).thenReturn(0 /* ratIndex */);
+        when(mCursor.getColumnIndex("totalTime")).thenReturn(1 /* totalTimeIndex */);
+        when(mCursor.moveToNext()).thenReturn(true, false);
+        when(mCursor.getString(0 /* ratIndex */)).thenReturn("LTE" /* inServiceRat */);
+        when(mCursor.getLong(1 /* totalTimeIndex */)).thenReturn(200000L /* inServiceTime */);
+
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        assertEquals(
+                actual.get(2 /*arrayIndex For In Service RAT Information */),
+                "IN_SERVICE RAT : " + "LTE" + ", Percentage = 20.00" + "%");
+    }
+
+    @Test
+    public void testInServiceDurationByRatWhenDataNotPresent() {
+        setUpForTestingGetInServiceDurationByRat();
+        setUpTotalTime();
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.RAT)).thenReturn(0 /* ratIndex */);
+        when(mCursor.getColumnIndex("totalTime")).thenReturn(1 /* totalTimeIndex */);
+        when(mCursor.moveToNext()).thenReturn(false);
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        assertEquals(actual.size(), 2 /* expectedArraySize */);
+    }
+
+    @Test
+    public void testInServiceDurationByRatWhenInvalidRatIndex() {
+        setUpForTestingGetInServiceDurationByRat();
+        setUpTotalTime();
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.RAT)).thenReturn(-1 /* ratIndex */);
+        when(mCursor.getColumnIndex("totalTime")).thenReturn(1 /* totalTimeIndex */);
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        assertEquals(actual.size(), 2 /* expectedArraySize */);
+    }
+
+    @Test
+    public void testInServiceDurationByRatWhenInvalidTimeIndex() {
+        setUpForTestingGetInServiceDurationByRat();
+        setUpTotalTime();
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.RAT)).thenReturn(0 /* ratIndex */);
+        when(mCursor.getColumnIndex("totalTime")).thenReturn(-1 /* totalTimeIndex */);
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        assertEquals(actual.size(), 2 /* expectedArraySize */);
+    }
+
+    @Test
+    public void testInServiceDurationByRatWhenBothIndexInvalid() {
+        setUpForTestingGetInServiceDurationByRat();
+        setUpTotalTime();
+        when(mCursor.getColumnIndex(ServiceStateAnalyticsTable.RAT)).thenReturn(-1 /* ratIndex */);
+        when(mCursor.getColumnIndex("totalTime")).thenReturn(-1 /* totalTimeIndex */);
+        ArrayList<String> actual = mServiceStateAnalyticsProvider.aggregate();
+        assertEquals(actual.size(), 2 /* expectedArraySize */);
+    }
+
+    @Test
+    public void testDeleteOldAndOverflowDataWhenLastDeletedDateEqualsToday() {
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        mServiceStateAnalyticsProvider.setDateOfDeletedRecordsServiceStateTable(dateToday);
+        TelephonyAnalytics.ServiceStateAnalytics.TimeStampedServiceState
+                mockTimeStampedServiceState =
+                        mock(
+                                TelephonyAnalytics.ServiceStateAnalytics.TimeStampedServiceState
+                                        .class);
+        mServiceStateAnalyticsProvider.insertDataToDb(
+                mockTimeStampedServiceState, 100L /* endTimeStamp */);
+    }
+
+    @After
+    public void tearDown() {
+        mServiceStateAnalyticsProvider = null;
+        mCursor = null;
+        mTelephonyAnalyticsUtil = null;
+        mContentValues = null;
+        mState = null;
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/analytics/SmsMmsAnalyticsProviderTest.java b/tests/telephonytests/src/com/android/internal/telephony/analytics/SmsMmsAnalyticsProviderTest.java
new file mode 100644
index 0000000..54acfeb
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/analytics/SmsMmsAnalyticsProviderTest.java
@@ -0,0 +1,406 @@
+/*
+ * 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.internal.telephony.analytics;
+
+import static android.os.Build.VERSION.INCREMENTAL;
+
+import static com.android.internal.telephony.analytics.TelephonyAnalyticsDatabase.DATE_FORMAT;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.concurrent.CountDownLatch;
+
+public class SmsMmsAnalyticsProviderTest {
+    @Mock TelephonyAnalyticsUtil mTelephonyAnalyticsUtil;
+
+    SmsMmsAnalyticsProvider mSmsMmsAnalyticsProvider;
+    private TelephonyAnalyticsUtil mMockTelephonyAnalyticsUtil;
+    private static final String[] SMS_MMS_INSERTION_PROJECTION = {
+        TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable._ID,
+        TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.COUNT
+    };
+
+    @Mock Cursor mCursor;
+    final String mCreateTableQuery =
+            "CREATE TABLE IF NOT EXISTS SmsMmsDataLogs("
+                    + "_id INTEGER PRIMARY KEY,"
+                    + "LogDate DATE ,"
+                    + "SmsMmsStatus TEXT DEFAULT '',"
+                    + "SmsMmsType TEXT DEFAULT '',"
+                    + "SlotID INTEGER , "
+                    + "RAT TEXT DEFAULT '',"
+                    + "FailureReason TEXT DEFAULT '',"
+                    + "ReleaseVersion TEXT DEFAULT '' , "
+                    + "Count INTEGER DEFAULT 1 );";
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mSmsMmsAnalyticsProvider = new SmsMmsAnalyticsProvider(mTelephonyAnalyticsUtil, 0);
+        mMockTelephonyAnalyticsUtil = mock(TelephonyAnalyticsUtil.class);
+        verify(mTelephonyAnalyticsUtil).createTable(mCreateTableQuery);
+    }
+
+    @Test
+    public void testCursor() {
+        assert (mCursor != null);
+        assert (mTelephonyAnalyticsUtil != null);
+    }
+
+    private ContentValues getContentValues(
+            String status, String type, String rat, String failureReason) {
+        ContentValues values = new ContentValues();
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.LOG_DATE, dateToday);
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SMS_MMS_STATUS, status);
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SMS_MMS_TYPE, type);
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.RAT, rat);
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SLOT_ID, 0);
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.FAILURE_REASON, failureReason);
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.RELEASE_VERSION, INCREMENTAL);
+
+        return values;
+    }
+
+    private void mockAndVerifyCall(String selection, String[] selectionArgs) {
+        when(mTelephonyAnalyticsUtil.getCursor(
+                        eq(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.TABLE_NAME),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull()))
+                .thenReturn(mCursor);
+
+        verify(mTelephonyAnalyticsUtil)
+                .getCursor(
+                        eq(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.TABLE_NAME),
+                        eq(SMS_MMS_INSERTION_PROJECTION),
+                        eq(selection),
+                        eq(selectionArgs),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull());
+    }
+
+    @Test
+    public void testFailureSms() {
+        String status = "Failure";
+        String type = "SMS Outgoing";
+        String rat = "LTE";
+        String failureReason = "SIM_ABSENT";
+        final String smsMmsInsertionFailureSelection =
+                TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.LOG_DATE
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SMS_MMS_STATUS
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SMS_MMS_TYPE
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.RAT
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SLOT_ID
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.FAILURE_REASON
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.RELEASE_VERSION
+                        + " = ? ";
+        ContentValues values = getContentValues(status, type, rat, failureReason);
+
+        String[] selectionArgs =
+                new String[] {
+                    values.getAsString(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.LOG_DATE),
+                    values.getAsString(
+                            TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SMS_MMS_STATUS),
+                    values.getAsString(
+                            TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SMS_MMS_TYPE),
+                    values.getAsString(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.RAT),
+                    values.getAsString(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SLOT_ID),
+                    values.getAsString(
+                            TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.FAILURE_REASON),
+                    values.getAsString(
+                            TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.RELEASE_VERSION)
+                };
+        mSmsMmsAnalyticsProvider.insertDataToDb(status, type, rat, failureReason);
+        mockAndVerifyCall(smsMmsInsertionFailureSelection, selectionArgs);
+    }
+
+    @Test
+    public void testSuccessSms() {
+        String status = "Success";
+        String type = "SMS Outgoing";
+        String rat = "LTE";
+        String failureReason = "SIM_ABSENT";
+
+        final String smsMmsInsertionSuccessSelection =
+                TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.LOG_DATE
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SMS_MMS_TYPE
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SMS_MMS_STATUS
+                        + " = ? AND "
+                        + TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SLOT_ID
+                        + " = ? ";
+
+        ContentValues values = getContentValues(status, type, rat, failureReason);
+        String[] selectionArgs =
+                new String[] {
+                    values.getAsString(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.LOG_DATE),
+                    values.getAsString(
+                            TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SMS_MMS_TYPE),
+                    values.getAsString(
+                            TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SMS_MMS_STATUS),
+                    values.getAsString(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.SLOT_ID)
+                };
+
+        mSmsMmsAnalyticsProvider.insertDataToDb(status, type, rat, failureReason);
+
+        mockAndVerifyCall(smsMmsInsertionSuccessSelection, selectionArgs);
+    }
+
+    @Test
+    public void testUpdateIfEntryExistsOtherwiseInsertWhenEntryNotExist() {
+        ContentValues values = new ContentValues();
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.COUNT, 5);
+        when(mCursor.moveToFirst()).thenReturn(false);
+        mSmsMmsAnalyticsProvider.updateIfEntryExistsOtherwiseInsert(mCursor, values);
+        verify(mTelephonyAnalyticsUtil)
+                .insert(eq(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.TABLE_NAME), eq(values));
+    }
+
+    @Test
+    public void testUpdateIfEntryExistsOtherwiseInsertWhenCursorNull() {
+        ContentValues values = new ContentValues();
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.COUNT, 5);
+        mSmsMmsAnalyticsProvider.updateIfEntryExistsOtherwiseInsert(null, values);
+        verify(mTelephonyAnalyticsUtil)
+                .insert(eq(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.TABLE_NAME), eq(values));
+    }
+
+    @Test
+    public void testUpdateIfEntryExistsOtherwiseInsertWhenEntryExists() {
+        ContentValues values = new ContentValues();
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.RAT, "LTE");
+
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex("_id")).thenReturn(0);
+        when(mCursor.getColumnIndex("Count")).thenReturn(1);
+        when(mCursor.getInt(0)).thenReturn(100);
+        when(mCursor.getInt(1)).thenReturn(2);
+
+        mSmsMmsAnalyticsProvider.updateIfEntryExistsOtherwiseInsert(mCursor, values);
+
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.COUNT, 3);
+
+        String updateSelection = TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable._ID + " = ? ";
+        String[] updateSelectionArgs = {String.valueOf(100)};
+
+        verify(mTelephonyAnalyticsUtil)
+                .update(
+                        eq(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.TABLE_NAME),
+                        eq(values),
+                        eq(updateSelection),
+                        eq(updateSelectionArgs));
+    }
+
+    @Test
+    public void testUpdateIfEntryExistsOtherwiseInsertWithInvalidIdColumnIndex() {
+        ContentValues values = new ContentValues();
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.RAT, "LTE");
+
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex("_id")).thenReturn(-1);
+        when(mCursor.getColumnIndex("Count")).thenReturn(1);
+        when(mCursor.getInt(0)).thenReturn(100);
+        when(mCursor.getInt(1)).thenReturn(2);
+        mSmsMmsAnalyticsProvider.updateIfEntryExistsOtherwiseInsert(mCursor, values);
+        verifyNoMoreInteractions(mTelephonyAnalyticsUtil);
+    }
+
+    @Test
+    public void testUpdateIfEntryExistsOtherwiseInsertWithInvalidCountColumnIndex() {
+        ContentValues values = new ContentValues();
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.RAT, "LTE");
+
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex("_id")).thenReturn(0);
+        when(mCursor.getColumnIndex("Count")).thenReturn(-1);
+        when(mCursor.getInt(0)).thenReturn(100);
+        when(mCursor.getInt(1)).thenReturn(2);
+        mSmsMmsAnalyticsProvider.updateIfEntryExistsOtherwiseInsert(mCursor, values);
+        verifyNoMoreInteractions(mTelephonyAnalyticsUtil);
+    }
+
+    @Test
+    public void testUpdateIfEntryExistsOtherwiseInsertWithInvalidColumnIndex() {
+        ContentValues values = new ContentValues();
+        values.put(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.RAT, "LTE");
+        when(mCursor.moveToFirst()).thenReturn(true);
+        when(mCursor.getColumnIndex("_id")).thenReturn(-1);
+        when(mCursor.getColumnIndex("Count")).thenReturn(-1);
+        when(mCursor.getInt(0)).thenReturn(100);
+        when(mCursor.getInt(1)).thenReturn(2);
+        mSmsMmsAnalyticsProvider.updateIfEntryExistsOtherwiseInsert(mCursor, values);
+        verifyNoMoreInteractions(mTelephonyAnalyticsUtil);
+    }
+
+    @Test
+    public void testDeleteWhenDateEqualsToday() {
+        String status = "Success";
+        String type = "SMS Outgoing";
+        String rat = "LTE";
+        String failureReason = "SIM_ABSENT";
+
+        String dateToday = DATE_FORMAT.format(Calendar.getInstance().toInstant());
+        mSmsMmsAnalyticsProvider.setDateOfDeletedRecordsSmsMmsTable(dateToday);
+        mSmsMmsAnalyticsProvider.insertDataToDb(status, type, rat, failureReason);
+        verify(mTelephonyAnalyticsUtil, times(0))
+                .delete(anyString(), anyString(), any(String[].class));
+    }
+
+    @Test
+    public void testDeleteWhenDateNotNullAndNotEqualsToday() {
+        String status = "Success";
+        String type = "SMS Outgoing";
+        String rat = "LTE";
+        String failureReason = "SIM_ABSENT";
+        String dateToday = "1965-10-12";
+        mSmsMmsAnalyticsProvider.setDateOfDeletedRecordsSmsMmsTable(dateToday);
+        mSmsMmsAnalyticsProvider.insertDataToDb(status, type, rat, failureReason);
+        verify(mTelephonyAnalyticsUtil, times(1))
+                .deleteOverflowAndOldData(anyString(), anyString(), anyString());
+    }
+
+    @Test
+    public void testAggregate() throws NoSuchFieldException, IllegalAccessException {
+        final CountDownLatch latch = new CountDownLatch(9);
+        Cursor mockCursor = mock(Cursor.class);
+        when(mMockTelephonyAnalyticsUtil.getCursor(
+                        eq(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.TABLE_NAME),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull()))
+                .thenReturn(mockCursor);
+        when(mMockTelephonyAnalyticsUtil.getCursor(
+                        eq(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.TABLE_NAME),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        anyString(),
+                        isNull(),
+                        isNull(),
+                        isNull()))
+                .thenReturn(mockCursor);
+        when(mMockTelephonyAnalyticsUtil.getCountFromCursor(eq(mockCursor)))
+                .thenReturn(
+                        80L /*totalOutgoingSms*/,
+                        70L /*totalIncomingSms*/,
+                        60L /*totalOutgoingMms*/,
+                        50L /*totalIncomingMms*/,
+                        40L /*totalFailedOutgoingSms*/,
+                        30L /*totalFailedIncomingSms*/,
+                        20L /*totalFailedOutgoingMms*/,
+                        10L /*totalFailedIncomingMms*/);
+
+        when(mockCursor.getColumnIndex("RAT")).thenReturn(0 /*ratIndex*/);
+        when(mockCursor.getColumnIndex("count")).thenReturn(1 /*countIndex*/);
+        when(mockCursor.moveToNext()).thenReturn(true, false);
+        when(mockCursor.getString(0 /*ratIndex*/)).thenReturn("LTE" /* RAT*/);
+        when(mockCursor.getInt(1 /*countIndex*/)).thenReturn(10 /* Count */);
+
+        SmsMmsAnalyticsProvider smsMmsAnalyticsProvider =
+                new SmsMmsAnalyticsProvider(mMockTelephonyAnalyticsUtil, 0 /* slotIndex */);
+        ArrayList<String> received = smsMmsAnalyticsProvider.aggregate();
+
+        verify(mMockTelephonyAnalyticsUtil, times(8))
+                .getCursor(
+                        eq(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.TABLE_NAME),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        isNull(),
+                        isNull(),
+                        isNull(),
+                        isNull());
+        verify(mMockTelephonyAnalyticsUtil, times(1))
+                .getCursor(
+                        eq(TelephonyAnalyticsDatabase.SmsMmsAnalyticsTable.TABLE_NAME),
+                        any(String[].class),
+                        anyString(),
+                        any(String[].class),
+                        anyString(),
+                        isNull(),
+                        isNull(),
+                        isNull());
+        verify(mMockTelephonyAnalyticsUtil, times(8)).getCountFromCursor(eq(mockCursor));
+        verify(mockCursor).getColumnIndex("RAT");
+        verify(mockCursor).getColumnIndex("count");
+
+        assertEquals(
+                "Total Outgoing Sms Count = 80",
+                received.get(0 /* array index for totalOutgoingSms */));
+        assertEquals(
+                "Total Incoming Sms Count = 70",
+                received.get(1 /* array index for totalIncomingSms */));
+        assertEquals(
+                "Failed Outgoing SMS Count = 40 Percentage failure rate for Outgoing SMS :50.00%",
+                received.get(2 /* array index for totalFailedOutgoingSms */));
+        assertEquals(
+                "Failed Incoming SMS Count = 30 Percentage failure rate for Incoming SMS :42.86%",
+                received.get(3 /* array index for totalFailedIncomingSms */));
+        assertEquals(
+                "Overall Fail Percentage = 46.67%",
+                received.get(4 /* array index for overall fail percentage */));
+        assertEquals(
+                "Failed SMS Count for RAT : LTE = 10, Percentage = 6.67%",
+                received.get(5 /* array index for  failedSmsTypeCountByRat */));
+    }
+
+    @After
+    public void tearDown() {
+        mCursor = null;
+        mTelephonyAnalyticsUtil = null;
+        mMockTelephonyAnalyticsUtil = null;
+        mSmsMmsAnalyticsProvider = null;
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
index 7ac3a17..22e4bc2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
@@ -21,6 +21,7 @@
 import static com.android.internal.telephony.data.AutoDataSwitchController.EVALUATION_REASON_DATA_SETTINGS_CHANGED;
 
 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.clearInvocations;
@@ -40,8 +41,11 @@
 import android.telephony.AccessNetworkConstants;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -69,14 +73,24 @@
     private static final int PHONE_2 = 1;
     private static final int SUB_2 = 2;
     private static final int MAX_RETRY = 5;
+    private static final int SCORE_TOLERANCE = 100;
+    private static final int GOOD_RAT_SIGNAL_SCORE = 200;
+    private static final int BAD_RAT_SIGNAL_SCORE = 50;
     // Mocked
     private AutoDataSwitchController.AutoDataSwitchControllerCallback mMockedPhoneSwitcherCallback;
 
+    // Real
+    private TelephonyDisplayInfo mGoodTelephonyDisplayInfo;
+    private TelephonyDisplayInfo mBadTelephonyDisplayInfo;
     private int mDefaultDataSub;
     private AutoDataSwitchController mAutoDataSwitchControllerUT;
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
+        mGoodTelephonyDisplayInfo = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR,
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false /*roaming*/);
+        mBadTelephonyDisplayInfo = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UMTS,
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false /*roaming*/);
         mMockedPhoneSwitcherCallback =
                 mock(AutoDataSwitchController.AutoDataSwitchControllerCallback.class);
 
@@ -95,13 +109,19 @@
             doReturn(mDisplayInfoController).when(phone).getDisplayInfoController();
             doReturn(mSignalStrengthController).when(phone).getSignalStrengthController();
             doReturn(mSignalStrength).when(phone).getSignalStrength();
+            doReturn(mDataNetworkController).when(phone).getDataNetworkController();
+            doReturn(mDataConfigManager).when(mDataNetworkController).getDataConfigManager();
             doAnswer(invocation -> phone.getSubId() == mDefaultDataSub)
                     .when(phone).isUserDataEnabled();
         }
-        doReturn(new int[mPhones.length]).when(mSubscriptionManagerService)
+        doReturn(new int[]{SUB_1, SUB_2}).when(mSubscriptionManagerService)
                 .getActiveSubIdList(true);
         doAnswer(invocation -> {
             int subId = (int) invocation.getArguments()[0];
+            return subId == SUB_1 ? PHONE_1 : PHONE_2;
+        }).when(mSubscriptionManagerService).getPhoneId(anyInt());
+        doAnswer(invocation -> {
+            int subId = (int) invocation.getArguments()[0];
 
             if (!SubscriptionManager.isUsableSubIdValue(subId)) return null;
 
@@ -111,11 +131,22 @@
         }).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
         replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
 
-        // Change resource overlay
+        // Change data config
         doReturn(true).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
-        doReturn(1L).when(mDataConfigManager)
+        doReturn(10000L).when(mDataConfigManager)
                 .getAutoDataSwitchAvailabilityStabilityTimeThreshold();
         doReturn(MAX_RETRY).when(mDataConfigManager).getAutoDataSwitchValidationMaxRetry();
+        doReturn(SCORE_TOLERANCE).when(mDataConfigManager).getAutoDataSwitchScoreTolerance();
+        doAnswer(invocation -> {
+            TelephonyDisplayInfo displayInfo = (TelephonyDisplayInfo) invocation.getArguments()[0];
+            SignalStrength signalStrength = (SignalStrength) invocation.getArguments()[1];
+            if (displayInfo == mGoodTelephonyDisplayInfo
+                    || signalStrength.getLevel() > SignalStrength.SIGNAL_STRENGTH_MODERATE) {
+                return GOOD_RAT_SIGNAL_SCORE;
+            }
+            return BAD_RAT_SIGNAL_SCORE;
+        }).when(mDataConfigManager).getAutoDataSwitchScore(any(TelephonyDisplayInfo.class),
+                any(SignalStrength.class));
 
         setDefaultDataSubId(SUB_1);
         doReturn(PHONE_1).when(mPhoneSwitcher).getPreferredDataPhoneId();
@@ -139,8 +170,10 @@
         // Verify attempting to switch
         verify(mMockedPhoneSwitcherCallback).onRequireValidation(PHONE_2, true/*needValidation*/);
 
-        // 1. Service state becomes not ideal - primary is available again
-        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        // 1. Service state becomes not ideal - secondary lost its advantage score
+        clearInvocations(mMockedPhoneSwitcherCallback);
+        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
+        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_POOR);
         processAllFutureMessages();
 
         verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
@@ -177,7 +210,41 @@
     }
 
     @Test
+    public void testCancelSwitch_onPrimary_rat_signalStrength() {
+        // 4.1 Display info and signal strength on secondary phone became bad
+        prepareIdealUsesNonDdsCondition();
+        processAllFutureMessages();
+        clearInvocations(mMockedPhoneSwitcherCallback);
+        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
+        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_MODERATE);
+        processAllFutureMessages();
+        verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+
+        // 4.2 Display info on default phone became good just as the secondary
+        prepareIdealUsesNonDdsCondition();
+        processAllFutureMessages();
+        clearInvocations(mMockedPhoneSwitcherCallback);
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        displayInfoChanged(PHONE_1, mGoodTelephonyDisplayInfo);
+        processAllFutureMessages();
+        verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+
+        // 4.3 Signal strength on default phone became just as good as the secondary
+        prepareIdealUsesNonDdsCondition();
+        processAllFutureMessages();
+        clearInvocations(mMockedPhoneSwitcherCallback);
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        signalStrengthChanged(PHONE_1, SignalStrength.SIGNAL_STRENGTH_GREAT);
+        processAllFutureMessages();
+        verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+    }
+
+    @Test
     public void testOnNonDdsSwitchBackToPrimary() {
+        // Disable Rat/SignalStrength based switch to test primary OOS based switch
+        doReturn(-1).when(mDataConfigManager).getAutoDataSwitchScoreTolerance();
+        mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
+                mPhoneSwitcher, mMockedPhoneSwitcherCallback);
         doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
 
         prepareIdealUsesNonDdsCondition();
@@ -232,6 +299,32 @@
     }
 
     @Test
+    public void testOnNonDdsSwitchBackToPrimary_rat_signalStrength() {
+        doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+
+        prepareIdealUsesNonDdsCondition();
+        // 4.1 Display info and signal strength on secondary phone became bad just as the default
+        // Expect no switch since both phone has the same score.
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
+        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_POOR);
+        processAllFutureMessages();
+        verify(mMockedPhoneSwitcherCallback, never()).onRequireValidation(anyInt(), anyBoolean());
+
+        clearInvocations(mMockedPhoneSwitcherCallback);
+        prepareIdealUsesNonDdsCondition();
+        // 4.2 Display info and signal strength on secondary phone became worse than the default.
+        // Expect to switch.
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        signalStrengthChanged(PHONE_1, SignalStrength.SIGNAL_STRENGTH_GREAT);
+        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
+        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_POOR);
+        processAllFutureMessages();
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+                true/*needValidation*/);
+    }
+
+    @Test
     public void testCancelSwitch_onSecondary() {
         doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
         prepareIdealUsesNonDdsCondition();
@@ -244,6 +337,7 @@
                 false/*needValidation*/);
 
         // cancel the switch back attempt due to secondary back to HOME
+        clearInvocations(mMockedPhoneSwitcherCallback);
         serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
         processAllFutureMessages();
 
@@ -251,6 +345,30 @@
     }
 
     @Test
+    public void testStabilityCheckOverride() {
+        // Starting stability check for switching to non-DDS
+        prepareIdealUsesNonDdsCondition();
+        processAllMessages();
+
+        // Switch success, but the previous stability check is still pending
+        doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+
+        // Display info and signal strength on secondary phone became worse than the default.
+        // Expect to switch back, and it should override the previous stability check
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        signalStrengthChanged(PHONE_1, SignalStrength.SIGNAL_STRENGTH_GREAT);
+        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
+        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_POOR);
+        // process all messages include the delayed message
+        processAllFutureMessages();
+
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+                true/*needValidation*/);
+        verify(mMockedPhoneSwitcherCallback, never()).onRequireValidation(PHONE_2,
+                true/*needValidation*/);
+    }
+
+    @Test
     public void testValidationFailedRetry() {
         prepareIdealUsesNonDdsCondition();
 
@@ -267,6 +385,8 @@
         // Change resource overlay
         doReturn(false).when(mDataConfigManager)
                 .isPingTestBeforeAutoDataSwitchRequired();
+        doReturn(-1 /*Disable signal based switch for easy mock*/).when(mDataConfigManager)
+                .getAutoDataSwitchScoreTolerance();
         mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
                 mPhoneSwitcher, mMockedPhoneSwitcherCallback);
 
@@ -327,13 +447,59 @@
                 eq(EVENT_SERVICE_STATE_CHANGED), eq(PHONE_2));
     }
 
+    @Test
+    public void testSubscriptionChangedUnregister() {
+        // Test single SIM loaded
+        int modemCount = 2;
+        doReturn(new int[]{SUB_2}).when(mSubscriptionManagerService)
+                .getActiveSubIdList(true);
+        mAutoDataSwitchControllerUT.notifySubscriptionsChanged();
+        processAllMessages();
+
+        // Verify unregister from both slots since only 1 visible SIM is insufficient for switching
+        verify(mDisplayInfoController, times(modemCount))
+                .unregisterForTelephonyDisplayInfoChanged(any());
+        verify(mSignalStrengthController, times(modemCount))
+                .unregisterForSignalStrengthChanged(any());
+        verify(mSST, times(modemCount)).unregisterForServiceStateChanged(any());
+
+        // Test single -> Duel
+        clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST);
+        doReturn(new int[]{SUB_1, SUB_2}).when(mSubscriptionManagerService)
+                .getActiveSubIdList(true);
+        mAutoDataSwitchControllerUT.notifySubscriptionsChanged();
+        processAllMessages();
+
+        // Verify register on both slots
+        for (int phoneId = 0; phoneId < modemCount; phoneId++) {
+            verify(mDisplayInfoController).registerForTelephonyDisplayInfoChanged(any(),
+                    eq(EVENT_DISPLAY_INFO_CHANGED), eq(phoneId));
+            verify(mSignalStrengthController).registerForSignalStrengthChanged(any(),
+                    eq(EVENT_SIGNAL_STRENGTH_CHANGED), eq(phoneId));
+            verify(mSST).registerForServiceStateChanged(any(),
+                    eq(EVENT_SERVICE_STATE_CHANGED), eq(phoneId));
+        }
+    }
+
+    @Test
+    public void testRatSignalStrengthSkipEvaluation() {
+        // Verify the secondary phone is OOS and its score(0) is too low to justify the evaluation
+        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
+        processAllFutureMessages();
+        verify(mMockedPhoneSwitcherCallback, never())
+                .onRequireCancelAnyPendingAutoSwitchValidation();
+        verify(mMockedPhoneSwitcherCallback, never()).onRequireValidation(anyInt(), anyBoolean());
+    }
+
     /**
      * Trigger conditions
      * 1. service state changes
-     * 2. data setting changes
+     * 2. telephony display info changes
+     * 3. signal strength changes
+     * 4. data setting changes
      *      - user toggle data
      *      - user toggle auto switch feature
-     * 3. default network changes
+     * 5. default network changes
      *      - current network lost
      *      - network become active on non-cellular network
      */
@@ -343,16 +509,42 @@
         serviceStateChanged(PHONE_1, NetworkRegistrationInfo
                 .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
 
-        // 2.1 User data enabled on primary SIM
+        // 2. telephony display info changes
+        displayInfoChanged(PHONE_2, mGoodTelephonyDisplayInfo);
+        displayInfoChanged(PHONE_1, mBadTelephonyDisplayInfo);
+
+        // 3. signal strength changes
+        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_GREAT);
+        signalStrengthChanged(PHONE_1, SignalStrength.SIGNAL_STRENGTH_POOR);
+
+        // 4.1 User data enabled on primary SIM
         doReturn(true).when(mPhone).isUserDataEnabled();
 
-        // 2.2 Auto switch feature is enabled
+        // 4.2 Auto switch feature is enabled
         doReturn(true).when(mPhone2).isDataAllowed();
 
-        // 3.1 No default network
+        // 5. No default network
         mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(null /*networkCapabilities*/);
     }
 
+    private void signalStrengthChanged(int phoneId, int level) {
+        SignalStrength ss = mock(SignalStrength.class);
+        doReturn(level).when(ss).getLevel();
+        doReturn(ss).when(mPhones[phoneId]).getSignalStrength();
+
+        Message msg = mAutoDataSwitchControllerUT.obtainMessage(EVENT_SIGNAL_STRENGTH_CHANGED);
+        msg.obj = new AsyncResult(phoneId, null, null);
+        mAutoDataSwitchControllerUT.sendMessage(msg);
+        processAllMessages();
+    }
+    private void displayInfoChanged(int phoneId, TelephonyDisplayInfo telephonyDisplayInfo) {
+        doReturn(telephonyDisplayInfo).when(mDisplayInfoController).getTelephonyDisplayInfo();
+
+        Message msg = mAutoDataSwitchControllerUT.obtainMessage(EVENT_DISPLAY_INFO_CHANGED);
+        msg.obj = new AsyncResult(phoneId, null, null);
+        mAutoDataSwitchControllerUT.sendMessage(msg);
+        processAllMessages();
+    }
     private void serviceStateChanged(int phoneId,
             @NetworkRegistrationInfo.RegistrationState int dataRegState) {
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
index d4a0804..3cc6a8b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
@@ -130,19 +130,19 @@
                         TelephonyManager.NETWORK_TYPE_LTE,
                         TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false/*isRoaming*/),
                 signalStrength)).isEqualTo(10227);
-        // Verify if entry contains any invalid negative scores, should yield -1.
+        // Verify if entry contains any invalid negative scores, should yield 0.
         doReturn(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN).when(signalStrength).getLevel();
         assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo(
                         TelephonyManager.NETWORK_TYPE_LTE,
                         TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/),
                 signalStrength))
-                .isEqualTo(-1/*INVALID_AUTO_DATA_SWITCH_SCORE*/);
+                .isEqualTo(0/*OUT_OF_SERVICE_AUTO_DATA_SWITCH_SCORE*/);
         // Verify non-existent entry should yield -1
         doReturn(SignalStrength.SIGNAL_STRENGTH_POOR).when(signalStrength).getLevel();
         assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo(
                         TelephonyManager.NETWORK_TYPE_EDGE,
                         TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/),
                 signalStrength))
-                .isEqualTo(-1/*INVALID_AUTO_DATA_SWITCH_SCORE*/);
+                .isEqualTo(0/*OUT_OF_SERVICE_AUTO_DATA_SWITCH_SCORE*/);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
index d96bac4..6d8449a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -115,6 +115,7 @@
 import com.android.internal.telephony.data.DataNetworkController.HandoverRule;
 import com.android.internal.telephony.data.DataRetryManager.DataRetryManagerCallback;
 import com.android.internal.telephony.data.LinkBandwidthEstimator.LinkBandwidthEstimatorCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.ims.ImsResolver;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 
@@ -158,7 +159,7 @@
     private DataNetworkControllerCallback mMockedDataNetworkControllerCallback;
     private DataRetryManagerCallback mMockedDataRetryManagerCallback;
     private ImsResolver mMockedImsResolver;
-
+    private DataStallRecoveryManager mMockedDataStallRecoveryManager;
     private ImsManager mMockedImsManager;
     private ImsMmTelManager mMockedImsMmTelManager;
     private ImsRcsManager mMockedImsRcsManager;
@@ -179,6 +180,10 @@
     private AccessNetworksManagerCallback mAccessNetworksManagerCallback;
     private LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback;
 
+    private boolean mIsNonTerrestrialNetwork = false;
+    private ArrayList<Integer> mCarrierSupportedSatelliteServices = new ArrayList<>();
+    private FeatureFlags mFeatureFlags;
+
     private final DataProfile mGeneralPurposeDataProfile = new DataProfile.Builder()
             .setApnSetting(new ApnSetting.Builder()
                     .setId(2163)
@@ -385,6 +390,17 @@
                             "PRIORITIZE_LATENCY", 1).getBytes()))
             .build();
 
+    private final DataProfile mMmsOnWlanDataProfile = new DataProfile.Builder()
+            .setApnSetting(new ApnSetting.Builder()
+                    .setEntryName("mms_wlan")
+                    .setApnName("mms_wlan")
+                    .setApnTypeBitmask(ApnSetting.TYPE_MMS)
+                    .setCarrierEnabled(true)
+                    .setNetworkTypeBitmask((int) TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN)
+                    .build())
+            .setPreferred(false)
+            .build();
+
     /** Data call response map. The first key is the transport type, the second key is the cid. */
     private final Map<Integer, Map<Integer, DataCallResponse>> mDataCallResponses = new HashMap<>();
 
@@ -609,6 +625,8 @@
                 .setRegistrationState(dataRegState)
                 .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
                 .setDataSpecificInfo(dsri)
+                .setIsNonTerrestrialNetwork(mIsNonTerrestrialNetwork)
+                .setAvailableServices(mCarrierSupportedSatelliteServices)
                 .build());
 
         ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
@@ -616,6 +634,8 @@
                 .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
                 .setRegistrationState(iwlanRegState)
                 .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setIsNonTerrestrialNetwork(mIsNonTerrestrialNetwork)
+                .setAvailableServices(mCarrierSupportedSatelliteServices)
                 .build());
 
         ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
@@ -726,6 +746,12 @@
                         .KEY_CAPABILITIES_EXEMPT_FROM_SINGLE_DC_CHECK_INT_ARRAY,
                 new int[]{NetworkCapabilities.NET_CAPABILITY_IMS});
 
+        mCarrierConfig.putLongArray(CarrierConfigManager.KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY,
+                new long[] {100, 100, 100, 100});
+        mCarrierConfig.putBooleanArray(
+                CarrierConfigManager.KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY,
+                new boolean[] {false, false, true, false, false});
+
         mContextFixture.putResource(com.android.internal.R.string.config_bandwidthEstimateSource,
                 "bandwidth_estimator");
 
@@ -747,9 +773,11 @@
         mMockedImsMmTelManager = Mockito.mock(ImsMmTelManager.class);
         mMockedImsRcsManager = Mockito.mock(ImsRcsManager.class);
         mMockedImsResolver = Mockito.mock(ImsResolver.class);
+        mMockedDataStallRecoveryManager = Mockito.mock(DataStallRecoveryManager.class);
         mMockedDataNetworkControllerCallback = Mockito.mock(DataNetworkControllerCallback.class);
         mMockedDataRetryManagerCallback = Mockito.mock(DataRetryManagerCallback.class);
         mMockSubInfo = Mockito.mock(SubscriptionInfo.class);
+        mFeatureFlags = Mockito.mock(FeatureFlags.class);
         when(mTelephonyComponentFactory.makeDataSettingsManager(any(Phone.class),
                 any(DataNetworkController.class), any(Looper.class),
                 any(DataSettingsManager.DataSettingsManagerCallback.class))).thenCallRealMethod();
@@ -831,7 +859,8 @@
         // to test, in this case, DataNetworkController. But since there are too many interactions
         // between DataNetworkController and its sub-modules, we intend to make those modules "real"
         // as well, except some modules below we replaced with mocks.
-        mDataNetworkControllerUT = new DataNetworkController(mPhone, Looper.myLooper());
+        mDataNetworkControllerUT = new DataNetworkController(mPhone, Looper.myLooper(),
+                mFeatureFlags);
         // First two come from DataServiceManager and the third comes from DataConfigManager which
         // is what we want to capture and assign to mCarrierConfigChangeListener
         verify(mCarrierConfigManager, times(3)).registerCarrierConfigChangeListener(any(),
@@ -858,6 +887,8 @@
         replaceInstance(DataNetworkController.class, "mAccessNetworksManager",
                 mDataNetworkControllerUT, mAccessNetworksManager);
         replaceInstance(ImsResolver.class, "sInstance", null, mMockedImsResolver);
+        replaceInstance(DataNetworkController.class, "mDataStallRecoveryManager",
+                mDataNetworkControllerUT, mMockedDataStallRecoveryManager);
 
         ArgumentCaptor<AccessNetworksManagerCallback> callbackCaptor =
                 ArgumentCaptor.forClass(AccessNetworksManagerCallback.class);
@@ -1090,7 +1121,7 @@
     }
 
     private void verifyInternetConnected() throws Exception {
-        verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkConnected(any());
+        verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(any());
         verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
     }
 
@@ -1150,7 +1181,7 @@
                 InetAddresses.parseNumericAddress(IPV4_ADDRESS),
                 InetAddresses.parseNumericAddress(IPV6_ADDRESS));
 
-        verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkConnected(any());
+        verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(any());
     }
 
     @Test
@@ -1168,7 +1199,7 @@
                 InetAddresses.parseNumericAddress(IPV4_ADDRESS),
                 InetAddresses.parseNumericAddress(IPV6_ADDRESS));
 
-        verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkConnected(any());
+        verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(any());
 
         // database updated/reloaded, causing data profile id change
         List<DataProfile> profiles = List.of(mDuplicatedGeneralPurposeDataProfile);
@@ -1205,7 +1236,7 @@
                 any(TelephonyNetworkRequest.class), anyInt(), anyBoolean());
 
         // verify the network still connects
-        verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkConnected(any());
+        verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(any());
 
         // A NOT_VCN_MANAGED request cannot be satisfied by the existing network, but will adopt the
         // same data profile
@@ -1216,7 +1247,7 @@
         processAllMessages();
 
         // verify the network still connects
-        verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkConnected(any());
+        verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(any());
         // verify we don't try to setup a separate network for the not_vcn_managed request
         dataNetworkList = getDataNetworks();
         assertThat(dataNetworkList).hasSize(1);
@@ -1279,7 +1310,7 @@
         mDataNetworkControllerUT.addNetworkRequest(request);
         processAllMessages();
         verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(true));
-        verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkConnected(any());
+        verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(any());
 
         int countOfCallbacks = dataNetworkControllerCallbacks.size();
 
@@ -1302,7 +1333,8 @@
         processAllMessages();
         verifyAllDataDisconnected();
         verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
-        verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkDisconnected();
+        verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(
+                eq(Collections.emptySet()));
         verify(mMockedDataNetworkControllerCallback).onPhysicalLinkStatusChanged(
                 eq(DataCallResponse.LINK_STATUS_INACTIVE));
     }
@@ -1466,7 +1498,8 @@
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
         verifyAllDataDisconnected();
         verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
-        verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkDisconnected();
+        verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(
+                eq(Collections.emptySet()));
         verify(mMockedDataNetworkControllerCallback).onPhysicalLinkStatusChanged(
                 eq(DataCallResponse.LINK_STATUS_INACTIVE));
 
@@ -1555,6 +1588,74 @@
     }
 
     @Test
+    public void testNonTerrestrialNetworkChangedWithoutDataSupport() throws Exception {
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        mIsNonTerrestrialNetwork = true;
+        // Data is not supported while using satellite
+        mCarrierSupportedSatelliteServices.add(NetworkRegistrationInfo.SERVICE_TYPE_VOICE);
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+
+        // Data with internet capability should not be allowed
+        // when the device is using non-terrestrial network
+        verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+        mIsNonTerrestrialNetwork = false;
+        mCarrierSupportedSatelliteServices.clear();
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+
+        // Verify data is restored.
+        verifyInternetConnected();
+    }
+
+    @Test
+    public void testNonTerrestrialNetworkWithDataSupport() throws Exception {
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        mIsNonTerrestrialNetwork = true;
+        // Data is supported while using satellite
+        mCarrierSupportedSatelliteServices.add(NetworkRegistrationInfo.SERVICE_TYPE_DATA);
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+
+        // Verify data is connected.
+        verifyInternetConnected();
+
+        mIsNonTerrestrialNetwork = false;
+        mCarrierSupportedSatelliteServices.clear();
+    }
+
+    @Test
+    public void testNonTerrestrialNetworkWithFlagDisabled() throws Exception {
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(false);
+
+        mIsNonTerrestrialNetwork = true;
+        // Data is not supported while using satellite
+        mCarrierSupportedSatelliteServices.add(NetworkRegistrationInfo.SERVICE_TYPE_VOICE);
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+
+        // As feature is disabled, data is connected.
+        verifyInternetConnected();
+
+        mIsNonTerrestrialNetwork = false;
+        mCarrierSupportedSatelliteServices.clear();
+    }
+
+
+    @Test
     public void testRoamingDataChanged() throws Exception {
         doReturn(true).when(mServiceState).getDataRoaming();
 
@@ -1578,7 +1679,6 @@
 
         // Verify data is restored.
         verifyInternetConnected();
-        Mockito.clearInvocations(mMockedDataNetworkControllerCallback);
 
         // Roaming data disabled
         mDataNetworkControllerUT.getDataSettingsManager().setDataRoamingEnabled(false);
@@ -1586,6 +1686,7 @@
 
         // Verify data is torn down.
         verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        Mockito.clearInvocations(mMockedDataNetworkControllerCallback);
 
         // Registration is back to HOME.
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
@@ -1744,9 +1845,13 @@
 
     @Test
     public void testIsDataEnabledOverriddenForApnDataDuringCall() throws Exception {
-        doReturn(1).when(mPhone).getSubId();
+        // Assume phone2 is the default data phone
+        Phone phone2 = Mockito.mock(Phone.class);
+        replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, phone2});
         doReturn(2).when(mSubscriptionManagerService).getDefaultDataSubId();
-        // Data disabled
+        doReturn(1).when(mPhone).getSubId();
+
+        // Data disabled on nonDDS
         mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
                 TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
 
@@ -1766,6 +1871,8 @@
 
         // Phone ringing
         doReturn(PhoneConstants.State.RINGING).when(mPhone).getState();
+        // Data is user enabled on DDS
+        doReturn(true).when(phone2).isUserDataEnabled();
         mDataNetworkControllerUT.addNetworkRequest(
                 createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
         processAllMessages();
@@ -2352,9 +2459,7 @@
                 new String[]{"source=EUTRAN, target=IWLAN, type=disallowed, capabilities=MMS|IMS",
                         "source=IWLAN, target=EUTRAN, type=disallowed, capabilities=MMS"});
         // Force data config manager to reload the carrier config.
-        mDataNetworkControllerUT.getDataConfigManager().obtainMessage(
-                1/*EVENT_CARRIER_CONFIG_CHANGED*/).sendToTarget();
-        processAllMessages();
+        carrierConfigChanged();
 
         testSetupImsDataNetwork();
 
@@ -2394,6 +2499,35 @@
     }
 
     @Test
+    public void testHandoverDataNetworkNotAllowedByPolicyDelayDueToVoiceCall() throws Exception {
+        // Config delay IMS tear down enabled
+        mCarrierConfig.putBoolean(CarrierConfigManager.KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL,
+                true);
+        mCarrierConfig.putStringArray(CarrierConfigManager.KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY,
+                new String[]{"source=EUTRAN, target=IWLAN, type=disallowed, capabilities=MMS|IMS"});
+        carrierConfigChanged();
+
+        testSetupImsDataNetwork();
+
+        // Ringing an active call, should delay handover tear down
+        doReturn(PhoneConstants.State.RINGING).when(mCT).getState();
+        updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
+                AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+
+        // Verify network is still connected due to active voice call
+        verify(mMockedWwanDataServiceManager, never()).deactivateDataCall(anyInt(),
+                eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+
+        // Verify tear down after call ends
+        doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
+        mDataNetworkControllerUT.obtainMessage(EVENT_VOICE_CALL_ENDED).sendToTarget();
+        processAllFutureMessages();
+
+        verify(mMockedWwanDataServiceManager).deactivateDataCall(anyInt(),
+                eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+    }
+
+    @Test
     public void testHandoverDataNetworkNotAllowedByRoamingPolicy() throws Exception {
         mCarrierConfig.putStringArray(CarrierConfigManager.KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY,
                 new String[]{"source=EUTRAN|NGRAN|IWLAN, target=EUTRAN|NGRAN|IWLAN, roaming=true, "
@@ -3139,7 +3273,7 @@
         processAllMessages();
 
         verify(mMockedDataNetworkControllerCallback)
-                .onInternetDataNetworkConnected(any());
+                .onConnectedInternetDataNetworksChanged(any());
         List<DataNetwork> dataNetworks = getDataNetworks();
         assertThat(dataNetworks).hasSize(1);
         assertThat(dataNetworks.get(0).getNetworkCapabilities().hasCapability(
@@ -4386,7 +4520,8 @@
         processAllMessages();
         verifyAllDataDisconnected();
         verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
-        verify(mMockedDataNetworkControllerCallback).onInternetDataNetworkDisconnected();
+        verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(
+                eq(Collections.emptySet()));
         verify(mMockedDataNetworkControllerCallback).onPhysicalLinkStatusChanged(
                 eq(DataCallResponse.LINK_STATUS_INACTIVE));
     }
@@ -4580,4 +4715,33 @@
         assertThat(dataNetworkList.get(1).getNetworkCapabilities().hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)).isTrue();
     }
+
+    @Test
+    public void testAllowBringUpWithDifferentDataProfileForWlan() throws Exception {
+        // Mock MMS preferred on WLAN
+        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
+                .getPreferredTransportByNetworkCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
+
+        // Setup a default cellular network that's capable of MMS
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS));
+        processAllMessages();
+        verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
+        verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
+
+        // Mock the designated MMS profile when WLAN is preferred
+        doReturn(mMmsOnWlanDataProfile).when(mDataProfileManager).getDataProfileForNetworkRequest(
+                any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_IWLAN),
+                anyBoolean());
+        setSuccessfulSetupDataResponse(mMockedWlanDataServiceManager,
+                createDataCallResponse(2, DataCallResponse.LINK_STATUS_ACTIVE));
+
+        // Verify the designated MMS profile is used to satisfy MMS request
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_MMS));
+        processAllMessages();
+        verifyConnectedNetworkHasDataProfile(mMmsOnWlanDataProfile);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
index 41a714c..9235e46 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -39,6 +39,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener;
 import android.net.vcn.VcnNetworkPolicyResult;
 import android.os.AsyncResult;
@@ -63,7 +64,9 @@
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
 import android.telephony.data.DataServiceCallback;
+import android.telephony.data.EpsQos;
 import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.Qos;
 import android.telephony.data.TrafficDescriptor;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -76,6 +79,7 @@
 import com.android.internal.telephony.data.DataEvaluation.DataAllowedReason;
 import com.android.internal.telephony.data.DataNetwork.DataNetworkCallback;
 import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
+import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
 import com.android.internal.telephony.data.LinkBandwidthEstimator.LinkBandwidthEstimatorCallback;
 import com.android.internal.telephony.metrics.DataCallSessionStats;
 
@@ -204,6 +208,11 @@
             .setTrafficDescriptor(new TrafficDescriptor(null, null))
             .build();
 
+    private final Qos mDefaultQos = new EpsQos(
+            new Qos.QosBandwidth(1000, 1),
+            new Qos.QosBandwidth(1000, 0),
+            9 /* QCI */);
+
     // Mocked classes
     private DataNetworkCallback mDataNetworkCallback;
     private DataCallSessionStats mDataCallSessionStats;
@@ -216,16 +225,21 @@
                     .build();
 
     private void setSuccessfulSetupDataResponse(DataServiceManager dsm, int cid) {
-        setSuccessfulSetupDataResponse(dsm, cid, Collections.emptyList());
+        setSuccessfulSetupDataResponse(dsm, cid, Collections.emptyList(), null);
     }
 
     private void setSuccessfulSetupDataResponse(DataServiceManager dsm, int cid,
             List<TrafficDescriptor> tds) {
+        setSuccessfulSetupDataResponse(dsm, cid, tds, null);
+    }
+
+    private void setSuccessfulSetupDataResponse(DataServiceManager dsm, int cid,
+            List<TrafficDescriptor> tds, Qos defaultQos) {
         doAnswer(invocation -> {
             final Message msg = (Message) invocation.getArguments()[10];
 
             DataCallResponse response = createDataCallResponse(
-                    cid, DataCallResponse.LINK_STATUS_ACTIVE, tds);
+                    cid, DataCallResponse.LINK_STATUS_ACTIVE, tds, defaultQos);
             msg.getData().putParcelable("data_call_response", response);
             msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
             msg.sendToTarget();
@@ -236,7 +250,7 @@
     }
 
     private DataCallResponse createDataCallResponse(int cid, int linkStatus,
-            List<TrafficDescriptor> tds) {
+            List<TrafficDescriptor> tds, Qos defaultQos) {
         return new DataCallResponse.Builder()
                 .setCause(0)
                 .setRetryDurationMillis(-1L)
@@ -260,6 +274,7 @@
                 .setPduSessionId(1)
                 .setQosBearerSessions(new ArrayList<>())
                 .setTrafficDescriptors(tds)
+                .setDefaultQos(defaultQos)
                 .build();
     }
 
@@ -410,7 +425,8 @@
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                 .build(), mPhone));
 
-        setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 123);
+        setSuccessfulSetupDataResponse(
+                mMockedWwanDataServiceManager, 123, Collections.emptyList(), mDefaultQos);
 
         mDataNetworkUT = new DataNetwork(mPhone, Looper.myLooper(), mDataServiceManagers,
                 mInternetDataProfile, networkRequestList,
@@ -470,6 +486,7 @@
         assertThat(pdcsList.get(0).getTransportType())
                 .isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
         assertThat(pdcsList.get(0).getLinkProperties()).isEqualTo(new LinkProperties());
+        assertThat(pdcsList.get(0).getDefaultQos()).isEqualTo(null);
 
         assertThat(pdcsList.get(1).getApnSetting()).isEqualTo(mInternetApnSetting);
         assertThat(pdcsList.get(1).getState()).isEqualTo(TelephonyManager.DATA_CONNECTED);
@@ -477,6 +494,7 @@
         assertThat(pdcsList.get(1).getNetworkType()).isEqualTo(TelephonyManager.NETWORK_TYPE_LTE);
         assertThat(pdcsList.get(1).getTransportType())
                 .isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        assertThat(pdcsList.get(1).getDefaultQos()).isEqualTo(mDefaultQos);
         assertThat(pdcsList.get(1).getLinkProperties().getAddresses().get(0))
                 .isEqualTo(InetAddresses.parseNumericAddress(IPV4_ADDRESS));
         assertThat(pdcsList.get(1).getLinkProperties().getAddresses().get(1))
@@ -1029,6 +1047,9 @@
         setupDataNetwork();
 
         setSuccessfulSetupDataResponse(mMockedWlanDataServiceManager, 456);
+        TelephonyNetworkAgent mockNetworkAgent = Mockito.mock(TelephonyNetworkAgent.class);
+        replaceInstance(DataNetwork.class, "mNetworkAgent",
+                mDataNetworkUT, mockNetworkAgent);
         // Now handover to IWLAN
         mDataNetworkUT.startHandover(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, null);
         processAllMessages();
@@ -1053,6 +1074,18 @@
                 .isEqualTo(TelephonyManager.DATA_HANDOVER_IN_PROGRESS);
         assertThat(pdcsList.get(3).getState()).isEqualTo(TelephonyManager.DATA_CONNECTED);
 
+        ArgumentCaptor<NetworkScore> networkScoreCaptor =
+                ArgumentCaptor.forClass(NetworkScore.class);
+        verify(mockNetworkAgent, times(2))
+                .sendNetworkScore(networkScoreCaptor.capture());
+        List<NetworkScore> networkScoreList = networkScoreCaptor.getAllValues();
+
+        assertThat(networkScoreList).hasSize(2);
+        assertThat(networkScoreList.get(0).getKeepConnectedReason())
+                .isEqualTo(NetworkScore.KEEP_CONNECTED_FOR_HANDOVER);
+        assertThat(networkScoreList.get(1).getKeepConnectedReason())
+                .isEqualTo(NetworkScore.KEEP_CONNECTED_NONE);
+
         // Now handover back to cellular
         Mockito.clearInvocations(mDataNetworkCallback);
         Mockito.clearInvocations(mLinkBandwidthEstimator);
@@ -1072,6 +1105,9 @@
     public void testHandoverFailed() throws Exception {
         setupDataNetwork();
 
+        TelephonyNetworkAgent mockNetworkAgent = Mockito.mock(TelephonyNetworkAgent.class);
+        replaceInstance(DataNetwork.class, "mNetworkAgent",
+                mDataNetworkUT, mockNetworkAgent);
         setFailedSetupDataResponse(mMockedWlanDataServiceManager,
                 DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE);
         // Now attempt to handover to IWLAN but fail it.
@@ -1101,6 +1137,18 @@
         assertThat(pdcsList.get(3).getLastCauseCode())
                 .isEqualTo(DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE);
 
+        ArgumentCaptor<NetworkScore> networkScoreCaptor =
+                ArgumentCaptor.forClass(NetworkScore.class);
+        verify(mockNetworkAgent, times(2))
+                .sendNetworkScore(networkScoreCaptor.capture());
+        List<NetworkScore> networkScoreList = networkScoreCaptor.getAllValues();
+
+        assertThat(networkScoreList).hasSize(2);
+        assertThat(networkScoreList.get(0).getKeepConnectedReason())
+                .isEqualTo(NetworkScore.KEEP_CONNECTED_FOR_HANDOVER);
+        assertThat(networkScoreList.get(1).getKeepConnectedReason())
+                .isEqualTo(NetworkScore.KEEP_CONNECTED_NONE);
+
         // Test source PDN lost during the HO, expect tear down after HO
         setFailedSetupDataResponse(mMockedWlanDataServiceManager,
                 DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE);
@@ -1218,6 +1266,88 @@
     }
 
     @Test
+    public void testRestrictedNetworkDataEnabled() throws Exception {
+        NetworkRequestList networkRequestList = new NetworkRequestList();
+        // Restricted network request
+        networkRequestList.add(new TelephonyNetworkRequest(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                .build(), mPhone));
+
+        // Data disabled
+        doReturn(false).when(mDataSettingsManager).isDataEnabled();
+        setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 123);
+
+        mDataNetworkUT = new DataNetwork(mPhone, Looper.myLooper(), mDataServiceManagers,
+                mInternetDataProfile, networkRequestList,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                DataAllowedReason.RESTRICTED_REQUEST, mDataNetworkCallback);
+        replaceInstance(DataNetwork.class, "mDataCallSessionStats",
+                mDataNetworkUT, mDataCallSessionStats);
+        mDataNetworkUT.sendMessage(18/*EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED*/,
+                new AsyncResult(null, new int[]{ADMIN_UID1, ADMIN_UID2}, null));
+        processAllMessages();
+
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)).isFalse();
+
+        ArgumentCaptor<DataSettingsManagerCallback> dataSettingsManagerCallbackCaptor =
+                ArgumentCaptor.forClass(DataSettingsManagerCallback.class);
+        verify(mDataSettingsManager).registerCallback(dataSettingsManagerCallbackCaptor.capture());
+
+        // Data enabled
+        doReturn(true).when(mDataSettingsManager).isDataEnabled();
+        dataSettingsManagerCallbackCaptor.getValue().onDataEnabledChanged(true,
+                TelephonyManager.DATA_ENABLED_REASON_USER, "");
+
+        // Network should become unrestricted.
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)).isTrue();
+    }
+
+    @Test
+    public void testRestrictedNetworkDataRoamingEnabled() throws Exception {
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+
+        NetworkRequestList networkRequestList = new NetworkRequestList();
+        // Restricted network request
+        networkRequestList.add(new TelephonyNetworkRequest(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                .build(), mPhone));
+
+        // Data roaming disabled
+        doReturn(false).when(mDataSettingsManager).isDataRoamingEnabled();
+        setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 123);
+
+        mDataNetworkUT = new DataNetwork(mPhone, Looper.myLooper(), mDataServiceManagers,
+                mInternetDataProfile, networkRequestList,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                DataAllowedReason.RESTRICTED_REQUEST, mDataNetworkCallback);
+        replaceInstance(DataNetwork.class, "mDataCallSessionStats",
+                mDataNetworkUT, mDataCallSessionStats);
+        mDataNetworkUT.sendMessage(18/*EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED*/,
+                new AsyncResult(null, new int[]{ADMIN_UID1, ADMIN_UID2}, null));
+        processAllMessages();
+
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)).isFalse();
+
+        ArgumentCaptor<DataSettingsManagerCallback> dataSettingsManagerCallbackCaptor =
+                ArgumentCaptor.forClass(DataSettingsManagerCallback.class);
+        verify(mDataSettingsManager).registerCallback(dataSettingsManagerCallbackCaptor.capture());
+
+        // Data roaming enabled
+        doReturn(true).when(mDataSettingsManager).isDataRoamingEnabled();
+        dataSettingsManagerCallbackCaptor.getValue().onDataRoamingEnabledChanged(true);
+
+        // Network should become unrestricted.
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)).isTrue();
+    }
+
+    @Test
     public void testVcnPolicyUpdated() throws Exception {
         setupDataNetwork();
 
@@ -1243,8 +1373,25 @@
 
     @Test
     public void testVcnPolicyTeardownRequested() throws Exception {
-        setupDataNetwork();
+        // 1. Tear down in Connecting state
+        doAnswer(invocation -> {
+            NetworkCapabilities nc = invocation.getArgument(0);
 
+            return new VcnNetworkPolicyResult(
+                    true /* isTearDownRequested */, nc);
+        }).when(mVcnManager).applyVcnNetworkPolicy(any(), any());
+        setupDataNetwork();
+        verify(mMockedWwanDataServiceManager).deactivateDataCall(anyInt(),
+                eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+
+        // 2. Tear down in Connected state
+        doAnswer(invocation -> {
+            NetworkCapabilities nc = invocation.getArgument(0);
+
+            return new VcnNetworkPolicyResult(
+                    false /* isTearDownRequested */, nc);
+        }).when(mVcnManager).applyVcnNetworkPolicy(any(), any());
+        setupDataNetwork();
         doAnswer(invocation -> {
             NetworkCapabilities nc = invocation.getArgument(0);
 
@@ -1831,7 +1978,7 @@
 
         // data state updated
         DataCallResponse response = createDataCallResponse(123,
-                DataCallResponse.LINK_STATUS_DORMANT, Collections.emptyList());
+                DataCallResponse.LINK_STATUS_DORMANT, Collections.emptyList(), null);
         mDataNetworkUT.sendMessage(8 /*EVENT_DATA_STATE_CHANGED*/, new AsyncResult(
                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN, List.of(response), null));
         processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
index 27271df..5c7e3ec 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
@@ -65,6 +65,7 @@
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -801,7 +802,6 @@
         assertThat(osAppId.getDifferentiator()).isEqualTo(1);
     }
 
-
     @Test
     public void testSetPreferredDataProfile() {
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(
@@ -817,7 +817,8 @@
         doReturn(dataProfile).when(internetNetwork).getDataProfile();
         doReturn(new DataNetworkController.NetworkRequestList(List.of(tnr)))
                 .when(internetNetwork).getAttachedNetworkRequestList();
-        mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(internetNetwork));
+        mDataNetworkControllerCallback.onConnectedInternetDataNetworksChanged(
+                Set.of(internetNetwork));
         processAllMessages();
 
         // Test See if the same one can be returned.
@@ -834,30 +835,44 @@
         doReturn(legacyRatDataProfile).when(legacyRatInternetNetwork).getDataProfile();
         doReturn(new DataNetworkController.NetworkRequestList(List.of(tnr)))
                 .when(legacyRatInternetNetwork).getAttachedNetworkRequestList();
-        mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(
+        mDataNetworkControllerCallback.onConnectedInternetDataNetworksChanged(Set.of(
                 // Because internetNetwork is torn down due to network type mismatch
                 legacyRatInternetNetwork));
         processAllMessages();
 
         assertThat(mDataProfileManagerUT.isDataProfilePreferred(legacyRatDataProfile)).isTrue();
 
-        // Test Another supl default internet network temporarily connected. Verify preferred
-        // doesn't change.
-        TelephonyNetworkRequest suplTnr = new TelephonyNetworkRequest(
+        // Test Another dun default internet network temporarily connected. Verify preferred
+        // doesn't change. Mock DUN | DEFAULT.
+        TelephonyNetworkRequest dunTnr = new TelephonyNetworkRequest(
                 new NetworkRequest.Builder()
-                        .addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
                         .build(), mPhone);
-        DataProfile suplDataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                suplTnr, TelephonyManager.NETWORK_TYPE_LTE, false);
-        DataNetwork suplInternetNetwork = Mockito.mock(DataNetwork.class);
-        doReturn(suplDataProfile).when(suplInternetNetwork).getDataProfile();
-        doReturn(new DataNetworkController.NetworkRequestList(List.of(suplTnr)))
-                .when(suplInternetNetwork).getAttachedNetworkRequestList();
-        mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(
-                legacyRatInternetNetwork, suplInternetNetwork));
+        DataProfile dunDataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+                dunTnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+        DataNetwork dunInternetNetwork = Mockito.mock(DataNetwork.class);
+        doReturn(dunDataProfile).when(dunInternetNetwork).getDataProfile();
+        doReturn(new DataNetworkController.NetworkRequestList(List.of(dunTnr)))
+                .when(dunInternetNetwork).getAttachedNetworkRequestList();
+        mDataNetworkControllerCallback.onConnectedInternetDataNetworksChanged(Set.of(
+                legacyRatInternetNetwork, dunInternetNetwork));
         processAllMessages();
 
         assertThat(mDataProfileManagerUT.isDataProfilePreferred(legacyRatDataProfile)).isTrue();
+
+        // Test a single dun default internet network temporarily connected. Verify preferred
+        // doesn't change. Mock DUN | DEFAULT and enforced single connection.
+        mDataNetworkControllerCallback.onConnectedInternetDataNetworksChanged(
+                Set.of(dunInternetNetwork));
+        processAllMessages();
+
+        assertThat(mDataProfileManagerUT.isDataProfilePreferred(legacyRatDataProfile)).isTrue();
+
+        // Test all internet networks disconnected. Verify preferred doesn't change.
+        mDataNetworkControllerCallback.onConnectedInternetDataNetworksChanged(
+                Collections.emptySet());
+
+        assertThat(mDataProfileManagerUT.isDataProfilePreferred(legacyRatDataProfile)).isTrue();
     }
 
     @Test
@@ -1055,6 +1070,7 @@
         DataProfile dataProfile1 = new DataProfile.Builder()
                 .setApnSetting(new ApnSetting.Builder()
                         .setEntryName("general")
+                        .setOperatorNumeric("123456")
                         .setApnName("apn")
                         .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS
                                 | ApnSetting.TYPE_SUPL | ApnSetting.TYPE_HIPRI)
@@ -1075,6 +1091,7 @@
         DataProfile dataProfile2 = new DataProfile.Builder()
                 .setApnSetting(new ApnSetting.Builder()
                         .setEntryName("XCAP")
+                        .setOperatorNumeric("123456")
                         .setApnName("apn")
                         .setApnTypeBitmask(ApnSetting.TYPE_XCAP)
                         .setUser("user")
@@ -1203,7 +1220,11 @@
         dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
         DataNetwork internetNetwork = Mockito.mock(DataNetwork.class);
         doReturn(dataProfile).when(internetNetwork).getDataProfile();
-        mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(internetNetwork));
+
+        doReturn(new DataNetworkController.NetworkRequestList(List.of(tnr)))
+                .when(internetNetwork).getAttachedNetworkRequestList();
+        mDataNetworkControllerCallback.onConnectedInternetDataNetworksChanged(
+                Set.of(internetNetwork));
         processAllMessages();
 
         // After internet connected, preferred APN should be set
@@ -1235,7 +1256,8 @@
         assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
 
         // Test removed data profile(user created after reset) shouldn't show up
-        mDataNetworkControllerCallback.onInternetDataNetworkConnected(List.of(internetNetwork));
+        mDataNetworkControllerCallback.onConnectedInternetDataNetworksChanged(
+                Set.of(internetNetwork));
         processAllMessages();
         //APN reset and removed GENERAL_PURPOSE_APN from APN DB
         mPreferredApnId = -1;
@@ -1342,6 +1364,7 @@
                 .setApnSetting(new ApnSetting.Builder()
                         .setEntryName(GENERAL_PURPOSE_APN)
                         .setId(1)
+                        .setOperatorNumeric(PLMN)
                         .setApnName(GENERAL_PURPOSE_APN)
                         .setProxyAddress("")
                         .setMmsProxyAddress("")
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
index 103b189..8372d8f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
@@ -33,8 +33,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.Intent;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.os.AsyncResult;
@@ -793,16 +791,15 @@
         processAllFutureMessages();
 
         // Verify scheduled via Alarm Manager
-        ArgumentCaptor<PendingIntent> pendingIntentArgumentCaptor =
-                ArgumentCaptor.forClass(PendingIntent.class);
-        verify(mAlarmManager).setExactAndAllowWhileIdle(anyInt(), anyLong(),
-                pendingIntentArgumentCaptor.capture());
+        ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor =
+                ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+        verify(mAlarmManager).setExactAndAllowWhileIdle(anyInt(), anyLong(), any(), any(), any(),
+                alarmListenerCaptor.capture());
 
-        // Verify starts retry attempt after receiving intent
-        PendingIntent pendingIntent = pendingIntentArgumentCaptor.getValue();
-        Intent intent = pendingIntent.getIntent();
-        mContext.sendBroadcast(intent);
-        processAllFutureMessages();
+        // Verify starts retry attempt after alarm fires.
+        AlarmManager.OnAlarmListener alarmListener = alarmListenerCaptor.getValue();
+        alarmListener.onAlarm();
+        processAllMessages();
 
         verify(mDataRetryManagerCallbackMock)
                 .onDataNetworkSetupRetry(any(DataSetupRetryEntry.class));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataServiceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataServiceManagerTest.java
index 88f642b..861b78c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataServiceManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataServiceManagerTest.java
@@ -155,7 +155,7 @@
                 message);
         waitAndVerifyResult(message, DataServiceCallback.RESULT_SUCCESS);
         verify(mSimulatedCommandsVerifier).setupDataCall(anyInt(), any(DataProfile.class),
-                anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
+                anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
                 any(Message.class));
     }
 
@@ -168,7 +168,7 @@
                 message);
         waitAndVerifyResult(message, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
         verify(mSimulatedCommandsVerifier, never()).setupDataCall(anyInt(), any(DataProfile.class),
-                anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
+                anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
                 any(Message.class));
     }
 
@@ -199,7 +199,7 @@
         mDataServiceManagerUT.setInitialAttachApn(mGeneralPurposeDataProfile, false, message);
         waitAndVerifyResult(message, DataServiceCallback.RESULT_SUCCESS);
         verify(mSimulatedCommandsVerifier).setInitialAttachApn(any(DataProfile.class),
-                anyBoolean(), any(Message.class));
+                any(Message.class));
     }
 
     @Test
@@ -209,7 +209,7 @@
         mDataServiceManagerUT.setInitialAttachApn(mGeneralPurposeDataProfile, false, message);
         waitAndVerifyResult(message, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
         verify(mSimulatedCommandsVerifier, never()).setInitialAttachApn(any(DataProfile.class),
-                anyBoolean(), any(Message.class));
+                any(Message.class));
     }
 
     @Test
@@ -218,7 +218,7 @@
         Message message = mHandler.obtainMessage(1234);
         mDataServiceManagerUT.setDataProfile(List.of(mGeneralPurposeDataProfile), false, message);
         waitAndVerifyResult(message, DataServiceCallback.RESULT_SUCCESS);
-        verify(mSimulatedCommandsVerifier).setDataProfile(any(DataProfile[].class), anyBoolean(),
+        verify(mSimulatedCommandsVerifier).setDataProfile(any(DataProfile[].class),
                 any(Message.class));
     }
 
@@ -229,7 +229,7 @@
         mDataServiceManagerUT.setDataProfile(List.of(mGeneralPurposeDataProfile), false, message);
         waitAndVerifyResult(message, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
         verify(mSimulatedCommandsVerifier, never()).setDataProfile(any(DataProfile[].class),
-                anyBoolean(), any(Message.class));
+                any(Message.class));
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
index 20cb73a..fc1bf0d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
@@ -29,7 +29,9 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.NonNull;
 import android.os.Looper;
+import android.os.Message;
 import android.os.PersistableBundle;
 import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
@@ -49,6 +51,8 @@
 import org.mockito.Mockito;
 
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -178,4 +182,26 @@
         callback.onUserDataEnabledChanged(true, "callingPackage");
         verify(mPhone).notifyDataEnabled(true, TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
     }
+
+    @Test
+    public void testNotifyDataEnabledFromNewValidSubId() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        mDataSettingsManagerUT.registerCallback(
+                new DataSettingsManagerCallback(mDataSettingsManagerUT::post) {
+                    @Override
+                    public void onDataEnabledChanged(boolean enabled,
+                            @TelephonyManager.DataEnabledChangedReason int reason,
+                            @NonNull String callingPackage) {
+                        latch.countDown();
+                    }
+                });
+
+        Message.obtain(mDataSettingsManagerUT, 4 /* EVENT_SUBSCRIPTIONS_CHANGED */, -1)
+                .sendToTarget();
+        Message.obtain(mDataSettingsManagerUT, 4 /* EVENT_SUBSCRIPTIONS_CHANGED */, 2)
+                .sendToTarget();
+        processAllMessages();
+
+        assertTrue(latch.await(1000, TimeUnit.MILLISECONDS));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
index 22cdaae..e1e238e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
@@ -29,6 +29,7 @@
 import android.content.Intent;
 import android.database.ContentObserver;
 import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -51,8 +52,7 @@
 import org.mockito.ArgumentCaptor;
 
 import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Set;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -154,12 +154,14 @@
         DataNetworkControllerCallback dataNetworkControllerCallback =
                 dataNetworkControllerCallbackCaptor.getAllValues().get(0);
 
-        if (isConnected) {
-            List<DataNetwork> dataprofile = new ArrayList<>();
-            dataNetworkControllerCallback.onInternetDataNetworkConnected(dataprofile);
-        } else {
-            dataNetworkControllerCallback.onInternetDataNetworkDisconnected();
+        DataNetwork network = mock(DataNetwork.class);
+        NetworkCapabilities netCaps = new NetworkCapabilities();
+        doReturn(netCaps).when(network).getNetworkCapabilities();
+        if (!isConnected) {
+            // A network that doesn't need to be tracked for validation
+            netCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         }
+        dataNetworkControllerCallback.onConnectedInternetDataNetworksChanged(Set.of(network));
         processAllMessages();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
index 807f44c..9e78afd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -632,7 +632,7 @@
         // A higher priority event occurring E.g. Phone1 has active IMS call on LTE.
         doReturn(mImsPhone).when(mPhone).getImsPhone();
         doReturn(true).when(mPhone).isUserDataEnabled();
-        doReturn(true).when(mPhone).isDataAllowed();
+        doReturn(true).when(mDataSettingsManager).isDataEnabled();
         mockImsRegTech(0, REGISTRATION_TECH_LTE);
         notifyPhoneAsInCall(mPhone);
 
@@ -853,7 +853,7 @@
         // Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled. This should
         // trigger data switch.
         doReturn(mImsPhone).when(mPhone2).getImsPhone();
-        doReturn(true).when(mPhone2).isDataAllowed();
+        doReturn(true).when(mDataSettingsManager2).isDataEnabled();
         mockImsRegTech(1, REGISTRATION_TECH_LTE);
         notifyPhoneAsInCall(mImsPhone);
 
@@ -882,7 +882,7 @@
         // Dialing shouldn't trigger switch because we give modem time to deal with the dialing call
         // first. Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled.
         doReturn(mImsPhone).when(mPhone2).getImsPhone();
-        doReturn(true).when(mPhone2).isDataAllowed();
+        doReturn(true).when(mDataSettingsManager2).isDataEnabled();
         mockImsRegTech(1, REGISTRATION_TECH_LTE);
         notifyPhoneAsInDial(mImsPhone);
 
@@ -916,7 +916,7 @@
         // Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled. This should
         // trigger data switch.
         doReturn(mImsPhone).when(mPhone2).getImsPhone();
-        doReturn(true).when(mPhone2).isDataAllowed();
+        doReturn(true).when(mDataSettingsManager2).isDataEnabled();
         mockImsRegTech(1, REGISTRATION_TECH_LTE);
         notifyPhoneAsInIncomingCall(mImsPhone);
 
@@ -943,7 +943,7 @@
 
         // Phone2 has active call, but data is turned off. So no data switching should happen.
         doReturn(mImsPhone).when(mPhone2).getImsPhone();
-        doReturn(true).when(mPhone2).isDataAllowed();
+        doReturn(true).when(mDataSettingsManager2).isDataEnabled();
         mockImsRegTech(1, REGISTRATION_TECH_IWLAN);
         notifyPhoneAsInCall(mImsPhone);
 
@@ -972,7 +972,7 @@
         // not trigger data switch.
         doReturn(mImsPhone).when(mPhone2).getImsPhone();
         doReturn(true).when(mPhone).isUserDataEnabled();
-        doReturn(true).when(mPhone2).isDataAllowed();
+        doReturn(true).when(mDataSettingsManager2).isDataEnabled();
         mockImsRegTech(1, REGISTRATION_TECH_CROSS_SIM);
         notifyPhoneAsInCall(mImsPhone);
 
@@ -1664,6 +1664,7 @@
 
         // 2.2 Auto switch feature is enabled
         doReturn(true).when(mPhone2).isDataAllowed();
+        doReturn(true).when(mDataSettingsManager2).isDataEnabled();
 
         // 3.1 No default network
         doReturn(null).when(mConnectivityManager).getNetworkCapabilities(any());
@@ -1743,7 +1744,7 @@
     private void notifyDataEnabled(boolean dataEnabled) {
         doReturn(true).when(mPhone).isUserDataEnabled();
         doReturn(dataEnabled).when(mDataSettingsManager).isDataEnabled();
-        doReturn(dataEnabled).when(mPhone2).isDataAllowed();
+        doReturn(dataEnabled).when(mDataSettingsManager2).isDataEnabled();
         mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(dataEnabled, 123 , "");
         if (mDataSettingsManagerCallbacks.size() > 1) {
             mDataSettingsManagerCallbacks.get(1).onDataEnabledChanged(dataEnabled, 123, "");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java
index 2c65b50..7095909 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java
@@ -137,12 +137,37 @@
 
     @Test
     @SmallTest
+    public void testGetDomainSelectionConnectionWhenImsPhoneNull() throws Exception {
+        setUpResolver(true, RADIO_HAL_VERSION_2_1);
+        mDsResolver.initialize(mDsService);
+        when(mPhone.getImsPhone()).thenReturn(null);
+
+        assertNull(mDsResolver.getDomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true));
+    }
+
+    @Test
+    @SmallTest
     public void testGetDomainSelectionConnectionWhenImsNotAvailable() throws Exception {
         setUpResolver(true, RADIO_HAL_VERSION_2_1);
         mDsResolver.initialize(mDsService);
         when(mPhone.isImsAvailable()).thenReturn(false);
+        when(mPhone.getImsPhone()).thenReturn(mImsPhone);
 
-        assertNull(mDsResolver.getDomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true));
+        assertNull(mDsResolver.getDomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, false));
+    }
+
+    @Test
+    @SmallTest
+    public void testGetDomainSelectionConnectionWhenImsNotAvailableForEmergencyCall()
+            throws Exception {
+        setUpResolver(true, RADIO_HAL_VERSION_2_1);
+        setUpController();
+        mDsResolver.initialize(mDsService);
+        when(mPhone.isImsAvailable()).thenReturn(false);
+        when(mPhone.getImsPhone()).thenReturn(mImsPhone);
+
+        assertNotNull(mDsResolver.getDomainSelectionConnection(mPhone,
+                SELECTOR_TYPE_CALLING, true));
     }
 
     @Test
@@ -152,6 +177,7 @@
         setUpController();
         mDsResolver.initialize(mDsService);
         when(mPhone.isImsAvailable()).thenReturn(true);
+        when(mPhone.getImsPhone()).thenReturn(mImsPhone);
 
         assertNotNull(mDsResolver.getDomainSelectionConnection(
                 mPhone, SELECTOR_TYPE_CALLING, true));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
index 2a8e4e2..7c1ac2c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -56,6 +56,7 @@
 import android.telephony.DisconnectCause;
 import android.telephony.EmergencyRegResult;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -1521,6 +1522,88 @@
         verify(phone0).exitEmergencyMode(any(Message.class));
     }
 
+    @Test
+    @SmallTest
+    public void testSaveKeyEmergencyCallbackModeSupportedBool() {
+        mContextFixture.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+        Phone phone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
+                false /* isRadioOn */);
+        when(phone.getSubId()).thenReturn(1);
+        setEcmSupportedConfig(phone, true);
+
+        EmergencyStateTracker testEst = setupEmergencyStateTracker(
+                false /* isSuplDdsSwitchRequiredForEmergencyCall */);
+
+        assertNotNull(testEst.startEmergencyCall(phone, TEST_CALL_ID, false));
+
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+        CarrierConfigManager cfgManager = (CarrierConfigManager) mContext
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+
+        verify(cfgManager).registerCarrierConfigChangeListener(any(),
+                listenerArgumentCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener carrierConfigChangeListener =
+                listenerArgumentCaptor.getAllValues().get(0);
+
+        // Verify carrier config for valid subscription
+        assertTrue(testEst.isEmergencyCallbackModeSupported());
+
+        // SIM removed
+        when(phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        setEcmSupportedConfig(phone, false);
+
+        // Verify default config for invalid subscription
+        assertFalse(testEst.isEmergencyCallbackModeSupported());
+
+        // Insert SIM again
+        when(phone.getSubId()).thenReturn(1);
+        setEcmSupportedConfig(phone, true);
+
+        // onCarrierConfigChanged with valid subscription
+        carrierConfigChangeListener.onCarrierConfigChanged(
+                phone.getPhoneId(), phone.getSubId(),
+                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+
+        // SIM removed again
+        when(phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        setEcmSupportedConfig(phone, false);
+
+        // onCarrierConfigChanged with invalid subscription
+        carrierConfigChangeListener.onCarrierConfigChanged(
+                phone.getPhoneId(), phone.getSubId(),
+                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+
+        // Verify saved config for valid subscription
+        assertTrue(testEst.isEmergencyCallbackModeSupported());
+
+        // Insert SIM again, but emergency callback mode not supported
+        when(phone.getSubId()).thenReturn(1);
+        setEcmSupportedConfig(phone, false);
+
+        // onCarrierConfigChanged with valid subscription
+        carrierConfigChangeListener.onCarrierConfigChanged(
+                phone.getPhoneId(), phone.getSubId(),
+                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+
+        // Verify carrier config for valid subscription
+        assertFalse(testEst.isEmergencyCallbackModeSupported());
+
+        // SIM removed again
+        when(phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        setEcmSupportedConfig(phone, false);
+
+        // onCarrierConfigChanged with invalid subscription
+        carrierConfigChangeListener.onCarrierConfigChanged(
+                phone.getPhoneId(), phone.getSubId(),
+                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+
+        // Verify saved config for valid subscription
+        assertFalse(testEst.isEmergencyCallbackModeSupported());
+    }
+
     private EmergencyStateTracker setupEmergencyStateTracker(
             boolean isSuplDdsSwitchRequiredForEmergencyCall) {
         doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
index 1c1ca0f..1f2cb15 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
@@ -36,11 +36,13 @@
 import com.android.internal.telephony.GsmCdmaPhone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 
 import java.util.ArrayList;
 import java.util.concurrent.Executor;
@@ -56,6 +58,7 @@
     private static final String TEST_DIAL_STRING_NON_EMERGENCY_NUMBER = "11976";
     private GsmMmiCode mGsmMmiCode;
     private GsmCdmaPhone mGsmCdmaPhoneUT;
+    @Mock private FeatureFlags mFeatureFlags;
 
     private final Executor mExecutor = Runnable::run;
 
@@ -64,7 +67,8 @@
         super.setUp(getClass().getSimpleName());
         doReturn(mExecutor).when(mContext).getMainExecutor();
         mGsmCdmaPhoneUT = new GsmCdmaPhone(mContext, mSimulatedCommands, mNotifier, true, 0,
-                PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
+                PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager,
+                mFeatureFlags);
         setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index cd9d9ad..9eafa09 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -1111,6 +1111,34 @@
         mContextFixture.addCallingOrSelfPermission("");
     }
 
+    @Test
+    @SmallTest
+    public void testClearPhoneNumberForSourceIms() {
+        // In reality the method under test runs in phone process so has MODIFY_PHONE_STATE
+        mContextFixture.addCallingOrSelfPermission(MODIFY_PHONE_STATE);
+        int subId = 1;
+        doReturn(subId).when(mPhone).getSubId();
+        doReturn(new SubscriptionInfoInternal.Builder().setId(subId).setSimSlotIndex(0)
+                .setCountryIso("gb").build()).when(mSubscriptionManagerService)
+                .getSubscriptionInfoInternal(subId);
+
+        // 1. Two valid phone number; 1st is set.
+        Uri[] associatedUris = new Uri[] {
+                Uri.parse("sip:+447539447777@ims.x.com"),
+                Uri.parse("tel:+447539446666")
+        };
+        mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
+
+        verify(mSubscriptionManagerService).setNumberFromIms(subId, "+447539447777");
+
+        mImsPhoneUT.clearPhoneNumberForSourceIms();
+
+        verify(mSubscriptionManagerService).setNumberFromIms(subId, "");
+
+        // Clean up
+        mContextFixture.addCallingOrSelfPermission("");
+    }
+
     /**
      * Verifies that valid radio technology is passed to RIL
      * when IMS registration state changes to registered.
@@ -1366,13 +1394,14 @@
 
         assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
 
-        // duplicated notification with the same suggested action
+        // verifies that duplicated notification with the same suggested action is invoked
         registrationCallback.onUnregistered(reasonInfo,
                 SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK, REGISTRATION_TECH_LTE);
         regInfo = mSimulatedCommands.getImsRegistrationInfo();
 
-        // verify that there is no update in the SimulatedCommands
-        assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+        assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+                && regInfo[1] == REGISTRATION_TECH_LTE
+                && regInfo[2] == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK);
 
         // unregistered with repeated error
         registrationCallback.onUnregistered(reasonInfo,
@@ -1390,14 +1419,15 @@
 
         assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
 
-        // duplicated notification with the same suggested action
+        // verfies that duplicated notification with the same suggested action is invoked
         registrationCallback.onUnregistered(reasonInfo,
                 SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT,
                 REGISTRATION_TECH_LTE);
         regInfo = mSimulatedCommands.getImsRegistrationInfo();
 
-        // verify that there is no update in the SimulatedCommands
-        assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+        assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+                && regInfo[1] == REGISTRATION_TECH_LTE
+                && regInfo[2] == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT);
 
         // unregistered with temporary error
         registrationCallback.onUnregistered(reasonInfo,
@@ -1408,6 +1438,19 @@
                 && regInfo[1] == REGISTRATION_TECH_LTE
                 && regInfo[2] == SUGGESTED_ACTION_NONE);
 
+        // reset the registration info saved in the SimulatedCommands
+        mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+        regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+        assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
+        // verfies that duplicated notification with temporary error is discarded
+        registrationCallback.onUnregistered(reasonInfo,
+                SUGGESTED_ACTION_NONE, REGISTRATION_TECH_LTE);
+        regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+        assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
         // verifies that reason codes except ImsReasonInfo.CODE_REGISTRATION_ERROR are discarded.
         reasonInfo = new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE,
                 ImsReasonInfo.CODE_UNSPECIFIED, "");
@@ -1425,7 +1468,7 @@
 
         assertTrue(regInfo[0] == 1 && regInfo[1] == 1 && regInfo[2] == 1);
 
-        // duplicated notification with the same suggested action
+        // verifies that duplicated notification with temporary error is discarded
         registrationCallback.onUnregistered(reasonInfo,
                 SUGGESTED_ACTION_NONE, REGISTRATION_TECH_NR);
         regInfo = mSimulatedCommands.getImsRegistrationInfo();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index d4e1b86..66bf482 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -51,6 +51,7 @@
 import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccController;
 import com.android.internal.telephony.uicc.UiccPort;
+import com.android.internal.telephony.uicc.UiccProfile;
 import com.android.internal.telephony.uicc.UiccSlot;
 
 import org.junit.After;
@@ -119,6 +120,9 @@
     @SmallTest
     public void onPullAtom_simSlotState_bothSimPresent() {
         // these have been tested extensively in SimSlotStateTest, here we verify atom generation
+        UiccProfile activeProfile = mock(UiccProfile.class);
+        doReturn(4).when(activeProfile).getNumApplications();
+        doReturn(activeProfile).when(mActivePort).getUiccProfile();
         doReturn(true).when(mPhysicalSlot).isActive();
         doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot).getCardState();
         doReturn(false).when(mPhysicalSlot).isEuicc();
@@ -126,7 +130,6 @@
         doReturn(CardState.CARDSTATE_PRESENT).when(mEsimSlot).getCardState();
         doReturn(true).when(mEsimSlot).isEuicc();
         doReturn(mActiveCard).when(mEsimSlot).getUiccCard();
-        doReturn(4).when(mActivePort).getNumApplications();
         doReturn(new UiccPort[] {mActivePort}).when(mActiveCard).getUiccPortList();
         doReturn(new UiccSlot[] {mPhysicalSlot, mEsimSlot}).when(mUiccController).getUiccSlots();
         doReturn(mPhysicalSlot).when(mUiccController).getUiccSlot(eq(0));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
index 3307813..aa24c46 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -1017,7 +1017,8 @@
         mSatelliteProvision1.isCanceled = false;
 
         mSatelliteProvision2 = new SatelliteProvision();
-        mSatelliteProvision2.resultCode = SatelliteProtoEnums.SATELLITE_SERVICE_NOT_PROVISIONED;
+        mSatelliteProvision2.resultCode =
+                SatelliteProtoEnums.SATELLITE_SERVICE_NOT_PROVISIONED;
         mSatelliteProvision2.provisioningTimeSec = 0;
         mSatelliteProvision2.isProvisionRequest = false;
         mSatelliteProvision2.isCanceled = true;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
index 22032ae..4393f1c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
@@ -196,7 +196,8 @@
     public void onSatelliteProvisionMetrics_withAtoms() throws Exception {
         SatelliteStats.SatelliteProvisionParams param =
                 new SatelliteStats.SatelliteProvisionParams.Builder()
-                        .setResultCode(SatelliteProtoEnums.SATELLITE_SERVICE_PROVISION_IN_PROGRESS)
+                        .setResultCode(
+                                SatelliteProtoEnums.SATELLITE_SERVICE_PROVISION_IN_PROGRESS)
                         .setProvisioningTimeSec(5 * 1000)
                         .setIsProvisionRequest(true)
                         .setIsCanceled(false)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
index 99a4a65..5d7eb9c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
@@ -31,6 +31,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
@@ -47,6 +48,7 @@
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.DataNetwork;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
@@ -57,6 +59,9 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
+import java.util.Collections;
+import java.util.Set;
+
 public class ServiceStateStatsTest extends TelephonyTest {
     private static final long START_TIME_MILLIS = 2000L;
     private static final int CARRIER1_ID = 1;
@@ -394,9 +399,12 @@
     public void onInternetDataNetworkDisconnected() throws Exception {
          // Using default service state for LTE
         mServiceStateStats.onServiceStateChanged(mServiceState);
+        // Set internet network connected
+        mServiceStateStats.onConnectedInternetDataNetworksChanged(Set.of(mock(DataNetwork.class)));
+        clearInvocations(mPersistAtomsStorage);
 
         mServiceStateStats.incTimeMillis(100L);
-        mServiceStateStats.onInternetDataNetworkDisconnected();
+        mServiceStateStats.onConnectedInternetDataNetworksChanged(Collections.emptySet());
         mServiceStateStats.incTimeMillis(200L);
         mServiceStateStats.conclude();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java
index 0a64ee3..c5c672c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java
@@ -29,6 +29,7 @@
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccPort;
+import com.android.internal.telephony.uicc.UiccProfile;
 import com.android.internal.telephony.uicc.UiccSlot;
 
 import org.junit.After;
@@ -71,8 +72,12 @@
         doReturn(CardState.CARDSTATE_PRESENT).when(mEsimSlot).getCardState();
         doReturn(true).when(mEsimSlot).isEuicc();
 
-        doReturn(0).when(mInactivePort).getNumApplications();
-        doReturn(4).when(mActivePort).getNumApplications();
+        UiccProfile inactiveProfile = mock(UiccProfile.class);
+        UiccProfile activeProfile = mock(UiccProfile.class);
+        doReturn(0).when(inactiveProfile).getNumApplications();
+        doReturn(4).when(activeProfile).getNumApplications();
+        doReturn(inactiveProfile).when(mInactivePort).getUiccProfile();
+        doReturn(activeProfile).when(mActivePort).getUiccProfile();
 
         doReturn(new UiccPort[]{mInactivePort}).when(mInactiveCard).getUiccPortList();
         doReturn(new UiccPort[]{mActivePort}).when(mActiveCard).getUiccPortList();
@@ -94,6 +99,8 @@
         assertEquals(0, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(0, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -108,6 +115,8 @@
         assertEquals(0, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(0, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -122,6 +131,8 @@
         assertEquals(0, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(0, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -136,6 +147,8 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(0, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -151,6 +164,8 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(1, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(0, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -166,6 +181,8 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(0, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -182,6 +199,8 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(0, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -197,6 +216,8 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -212,6 +233,8 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -227,11 +250,31 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(1, state.numActiveSims);
         assertEquals(1, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
     @Test
     @SmallTest
+    public void testSingleSim_esimCardWithMultipleProfiles() {
+        doReturn(mActiveCard).when(mEsimSlot).getUiccCard();
+        doReturn(new UiccPort[]{mActivePort, mActivePort}).when(mActiveCard).getUiccPortList();
+        setupSingleSim(mEsimSlot);
+
+        SimSlotState state = SimSlotState.getCurrentState();
+        boolean isMultiSim = SimSlotState.isMultiSim();
+
+        assertEquals(1, state.numActiveSlots);
+        assertEquals(2, state.numActiveSims);
+        assertEquals(2, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(1, state.numActiveMepSlots);
+        assertTrue(isMultiSim);
+    }
+
+    @Test
+    @SmallTest
     public void testDsdsSingleSimMode_noSimCard() {
         setupDualSim(mEmptySlot, mInactiveSlot);
 
@@ -241,6 +284,8 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(0, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -255,6 +300,8 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(1, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(0, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -270,6 +317,8 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -285,6 +334,8 @@
         assertEquals(1, state.numActiveSlots);
         assertEquals(1, state.numActiveSims);
         assertEquals(1, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -300,6 +351,8 @@
         assertEquals(2, state.numActiveSlots);
         assertEquals(0, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -315,6 +368,8 @@
         assertEquals(2, state.numActiveSlots);
         assertEquals(1, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
@@ -330,11 +385,31 @@
         assertEquals(2, state.numActiveSlots);
         assertEquals(1, state.numActiveSims);
         assertEquals(1, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim);
     }
 
     @Test
     @SmallTest
+    public void testDsds_esimCardWithMultipleProfiles() {
+        doReturn(mActiveCard).when(mEsimSlot).getUiccCard();
+        doReturn(new UiccPort[]{mActivePort, mActivePort}).when(mActiveCard).getUiccPortList();
+        setupDualSim(mEmptySlot, mEsimSlot);
+
+        SimSlotState state = SimSlotState.getCurrentState();
+        boolean isMultiSim = SimSlotState.isMultiSim();
+
+        assertEquals(2, state.numActiveSlots);
+        assertEquals(2, state.numActiveSims);
+        assertEquals(2, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(1, state.numActiveMepSlots);
+        assertTrue(isMultiSim);
+    }
+
+    @Test
+    @SmallTest
     public void testDsds_physicalAndEsimCardWithProfile() {
         doReturn(mActiveCard).when(mEsimSlot).getUiccCard();
         setupDualSim(mPhysicalSlot, mEsimSlot);
@@ -345,6 +420,8 @@
         assertEquals(2, state.numActiveSlots);
         assertEquals(2, state.numActiveSims);
         assertEquals(1, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertTrue(isMultiSim);
     }
 
@@ -359,6 +436,8 @@
         assertEquals(2, state.numActiveSlots);
         assertEquals(2, state.numActiveSims);
         assertEquals(0, state.numActiveEsims);
+        assertEquals(0, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertTrue(isMultiSim);
     }
 
@@ -382,6 +461,8 @@
         assertEquals(2, state.numActiveSlots);
         assertEquals(1, state.numActiveSims);
         assertEquals(1, state.numActiveEsims);
+        assertEquals(1, state.numActiveEsimSlots);
+        assertEquals(0, state.numActiveMepSlots);
         assertFalse(isMultiSim); // one Uicc Port does not have active sim profile
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
index b358b6d..5412992 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -76,6 +76,7 @@
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccPort;
+import com.android.internal.telephony.uicc.UiccProfile;
 import com.android.internal.telephony.uicc.UiccSlot;
 
 import org.junit.After;
@@ -178,8 +179,13 @@
         doReturn(true).when(mEsimSlot).isActive();
         doReturn(CardState.CARDSTATE_PRESENT).when(mEsimSlot).getCardState();
         doReturn(true).when(mEsimSlot).isEuicc();
-        doReturn(0).when(mInactivePort).getNumApplications();
-        doReturn(4).when(mActivePort).getNumApplications();
+
+        UiccProfile inactiveProfile = mock(UiccProfile.class);
+        UiccProfile activeProfile = mock(UiccProfile.class);
+        doReturn(0).when(inactiveProfile).getNumApplications();
+        doReturn(4).when(activeProfile).getNumApplications();
+        doReturn(inactiveProfile).when(mInactivePort).getUiccProfile();
+        doReturn(activeProfile).when(mActivePort).getUiccProfile();
 
         doReturn(new UiccSlot[] {mPhysicalSlot}).when(mUiccController).getUiccSlots();
         doReturn(mPhysicalSlot).when(mUiccController).getUiccSlot(eq(0));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java
index baa00c1..76cd4ad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java
@@ -280,7 +280,7 @@
     public void testReportIncomingDatagramCount() {
         mTestStats.initializeParams();
 
-        int result = SatelliteManager.SATELLITE_ERROR_NONE;
+        int result = SatelliteManager.SATELLITE_RESULT_SUCCESS;
         for (int i = 0; i < 10; i++) {
             mControllerMetricsStatsUT.reportIncomingDatagramCount(result);
         }
@@ -303,7 +303,7 @@
         assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
         mTestStats.initializeParams();
 
-        result = SatelliteManager.SATELLITE_SERVER_ERROR;
+        result = SatelliteManager.SATELLITE_RESULT_SERVER_ERROR;
         for (int i = 0; i < 10; i++) {
             mControllerMetricsStatsUT.reportIncomingDatagramCount(result);
         }
@@ -326,7 +326,7 @@
         assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
         mTestStats.initializeParams();
 
-        result = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+        result = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
         for (int i = 0; i < 10; i++) {
             mControllerMetricsStatsUT.reportIncomingDatagramCount(result);
         }
@@ -354,7 +354,7 @@
     public void testReportProvisionCount() {
         mTestStats.initializeParams();
 
-        int result = SatelliteManager.SATELLITE_ERROR_NONE;
+        int result = SatelliteManager.SATELLITE_RESULT_SUCCESS;
         for (int i = 0; i < 10; i++) {
             mControllerMetricsStatsUT.reportProvisionCount(result);
         }
@@ -377,7 +377,7 @@
         assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
         mTestStats.initializeParams();
 
-        result = SatelliteManager.SATELLITE_SERVER_ERROR;
+        result = SatelliteManager.SATELLITE_RESULT_SERVER_ERROR;
         for (int i = 0; i < 10; i++) {
             mControllerMetricsStatsUT.reportProvisionCount(result);
         }
@@ -400,7 +400,7 @@
         assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
         mTestStats.initializeParams();
 
-        result = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+        result = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
         for (int i = 0; i < 10; i++) {
             mControllerMetricsStatsUT.reportProvisionCount(result);
         }
@@ -428,7 +428,7 @@
     public void testReportDeprovisionCount() {
         mTestStats.initializeParams();
 
-        int result = SatelliteManager.SATELLITE_ERROR_NONE;
+        int result = SatelliteManager.SATELLITE_RESULT_SUCCESS;
         for (int i = 0; i < 10; i++) {
             mControllerMetricsStatsUT.reportDeprovisionCount(result);
         }
@@ -451,7 +451,7 @@
         assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
         mTestStats.initializeParams();
 
-        result = SatelliteManager.SATELLITE_SERVER_ERROR;
+        result = SatelliteManager.SATELLITE_RESULT_SERVER_ERROR;
         for (int i = 0; i < 10; i++) {
             mControllerMetricsStatsUT.reportDeprovisionCount(result);
         }
@@ -474,7 +474,7 @@
         assertEquals(0, mTestStats.mTotalBatteryChargedTimeSec);
         mTestStats.initializeParams();
 
-        result = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+        result = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
         for (int i = 0; i < 10; i++) {
             mControllerMetricsStatsUT.reportDeprovisionCount(result);
         }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
index bc1d767..c5f1c45 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -20,14 +20,21 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.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.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -73,6 +80,7 @@
     @Mock private DatagramReceiver mMockDatagramReceiver;
     @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
     @Mock private ControllerMetricsStats mMockControllerMetricsStats;
+    @Mock private SatelliteSessionController mMockSatelliteSessionController;
 
     /** Variables required to send datagram in the unit tests. */
     LinkedBlockingQueue<Integer> mResultListener;
@@ -93,6 +101,8 @@
                 mMockSatelliteModemInterface);
         replaceInstance(ControllerMetricsStats.class, "sInstance", null,
                 mMockControllerMetricsStats);
+        replaceInstance(SatelliteSessionController.class, "sInstance", null,
+                mMockSatelliteSessionController);
 
         mDatagramDispatcherUT = DatagramDispatcher.make(mContext, Looper.myLooper(),
                 mMockDatagramController);
@@ -129,28 +139,96 @@
             return null;
         }).when(mMockSatelliteModemInterface).sendSatelliteDatagram(any(SatelliteDatagram.class),
                 anyBoolean(), anyBoolean(), any(Message.class));
+        doReturn(true).when(mMockDatagramController)
+                .needsWaitingForSatelliteConnected();
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState())
+                .thenReturn(DatagramController.DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT);
+        mResultListener.clear();
 
         mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
                 true, mResultListener::offer);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
+        verify(mMockSatelliteSessionController).onSatelliteDatagramsTransferRequested();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
 
+        mDatagramDispatcherUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
         processAllMessages();
 
         mInOrder.verify(mMockDatagramController).isPollingInIdleState();
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         verifyNoMoreInteractions(mMockDatagramController);
+        verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
+                any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+        assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
 
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
+
+        clearInvocations(mMockSatelliteModemInterface);
+        clearInvocations(mMockDatagramController);
+        mResultListener.clear();
+
+        clearInvocations(mMockSatelliteModemInterface);
+        clearInvocations(mMockDatagramController);
+        mResultListener.clear();
+        mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+                true, mResultListener::offer);
+        processAllMessages();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
+        assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        moveTimeForward(DatagramController.DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT);
+        processAllMessages();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        verifyZeroInteractions(mMockDatagramController);
+        assertEquals(1, mResultListener.size());
+        assertThat(mResultListener.peek()).isEqualTo(
+                SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE);
+        assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        mResultListener.clear();
+        mDatagramDispatcherUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        assertEquals(0, mResultListener.size());
+
+        clearInvocations(mMockSatelliteModemInterface);
+        clearInvocations(mMockDatagramController);
+        mResultListener.clear();
+        mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+                true, mResultListener::offer);
+        processAllMessages();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
+        assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+        assertEquals(0, mResultListener.size());
+
+        mDatagramDispatcherUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+        processAllMessages();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        assertEquals(1, mResultListener.size());
+        assertThat(mResultListener.peek()).isEqualTo(
+                SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
+        assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
     }
 
     @Test
@@ -162,7 +240,7 @@
             mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
                             new AsyncResult(message.obj, null,
                                     new SatelliteManager.SatelliteException(
-                                            SatelliteManager.SATELLITE_SERVICE_ERROR)))
+                                            SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR)))
                     .sendToTarget();
             return null;
         }).when(mMockSatelliteModemInterface).sendSatelliteDatagram(any(SatelliteDatagram.class),
@@ -173,22 +251,24 @@
 
         processAllMessages();
 
+        mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
         mInOrder.verify(mMockDatagramController).isPollingInIdleState();
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED), eq(0),
-                        eq(SatelliteManager.SATELLITE_SERVICE_ERROR));
+                        eq(SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         verifyNoMoreInteractions(mMockDatagramController);
 
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_SERVICE_ERROR);
+        assertThat(mResultListener.peek()).isEqualTo(
+                SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
     }
 
     @Test
@@ -201,23 +281,24 @@
 
         processAllMessages();
 
+        mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
         mInOrder.verify(mMockDatagramController).isPollingInIdleState();
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED), eq(0),
-                        eq(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE));
+                        eq(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         verifyNoMoreInteractions(mMockDatagramController);
 
         assertThat(mResultListener.peek())
-                .isEqualTo(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                .isEqualTo(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
     }
 
     @Test
@@ -239,22 +320,23 @@
 
         processAllMessages();
 
+        mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
         mInOrder.verify(mMockDatagramController).isPollingInIdleState();
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         verifyNoMoreInteractions(mMockDatagramController);
 
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
     }
 
     @Test
@@ -267,7 +349,7 @@
             mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
                             new AsyncResult(message.obj, null,
                                     new SatelliteManager.SatelliteException(
-                                            SatelliteManager.SATELLITE_SERVICE_ERROR)))
+                                            SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR)))
                     .sendToTarget();
             return null;
         }).when(mPhone).sendSatelliteDatagram(any(Message.class), any(SatelliteDatagram.class),
@@ -278,28 +360,30 @@
 
         processAllMessages();
 
+        mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
         mInOrder.verify(mMockDatagramController).isPollingInIdleState();
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED), eq(0),
-                        eq(SatelliteManager.SATELLITE_SERVICE_ERROR));
+                        eq(SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         verifyNoMoreInteractions(mMockDatagramController);
 
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_SERVICE_ERROR);
+        assertThat(mResultListener.peek()).isEqualTo(
+                SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
     }
 
     @Test
     public void testSendSatelliteDatagram_DemoMode_Align_Success() throws Exception {
         mTestDemoModeDatagramDispatcher.setDemoMode(true);
-        mTestDemoModeDatagramDispatcher.onDeviceAlignedWithSatellite(true);
+        mTestDemoModeDatagramDispatcher.setDeviceAlignedWithSatellite(true);
         doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
         doAnswer(invocation -> {
@@ -320,18 +404,18 @@
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
         mTestDemoModeDatagramDispatcher.setDemoMode(false);
-        mTestDemoModeDatagramDispatcher.onDeviceAlignedWithSatellite(false);
+        mTestDemoModeDatagramDispatcher.setDeviceAlignedWithSatellite(false);
     }
 
     @Test
@@ -339,7 +423,7 @@
         long previousTimer = mTestDemoModeDatagramDispatcher.getSatelliteAlignedTimeoutDuration();
         mTestDemoModeDatagramDispatcher.setDemoMode(true);
         mTestDemoModeDatagramDispatcher.setDuration(TEST_EXPIRE_TIMER_SATELLITE_ALIGN);
-        mTestDemoModeDatagramDispatcher.onDeviceAlignedWithSatellite(false);
+        mTestDemoModeDatagramDispatcher.setDeviceAlignedWithSatellite(false);
 
         doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
@@ -359,19 +443,20 @@
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         processAllFutureMessages();
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED),
-                        anyInt(), eq(SatelliteManager.SATELLITE_NOT_REACHABLE));
+                        anyInt(), eq(SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_NOT_REACHABLE);
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
+        assertThat(mResultListener.peek()).isEqualTo(
+                SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE);
         mTestDemoModeDatagramDispatcher.setDemoMode(false);
-        mTestDemoModeDatagramDispatcher.onDeviceAlignedWithSatellite(false);
+        mTestDemoModeDatagramDispatcher.setDeviceAlignedWithSatellite(false);
         mTestDemoModeDatagramDispatcher.setDuration(previousTimer);
     }
 
@@ -398,22 +483,22 @@
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
 
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
 
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
 
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
 
         mTestDemoModeDatagramDispatcher.setDemoMode(false);
-        mTestDemoModeDatagramDispatcher.onDeviceAlignedWithSatellite(false);
+        mTestDemoModeDatagramDispatcher.setDeviceAlignedWithSatellite(false);
     }
 
     @Test
@@ -424,6 +509,7 @@
                 true, mResultListener::offer);
         processAllMessages();
         // As modem is busy receiving datagrams, sending datagram did not proceed further.
+        mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
         mInOrder.verify(mMockDatagramController).isPollingInIdleState();
         verifyNoMoreInteractions(mMockDatagramController);
     }
@@ -449,11 +535,11 @@
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(anyInt(),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED),
-                        eq(1), eq(SatelliteManager.SATELLITE_REQUEST_ABORTED));
+                        eq(1), eq(SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED));
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(anyInt(),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
-                        eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
     }
 
     @Test
@@ -466,7 +552,7 @@
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(anyInt(),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
-                        eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
     }
 
     private static class TestDatagramDispatcher extends DatagramDispatcher {
@@ -483,8 +569,8 @@
         }
 
         @Override
-        protected  void onDeviceAlignedWithSatellite(boolean isAligned) {
-            super.onDeviceAlignedWithSatellite(isAligned);
+        protected  void setDeviceAlignedWithSatellite(boolean isAligned) {
+            super.setDeviceAlignedWithSatellite(isAligned);
         }
 
         @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
index 1c3777d..18f89db 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -18,19 +18,15 @@
 
 import static com.android.internal.telephony.satellite.DatagramController.SATELLITE_ALIGN_TIMEOUT;
 
-import android.annotation.NonNull;
-import android.content.Context;
-import android.provider.Telephony;
-import android.telephony.satellite.ISatelliteDatagramCallback;
-import android.test.mock.MockContentResolver;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
@@ -38,15 +34,23 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
+import android.content.Context;
 import android.os.AsyncResult;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.IBinder;
 import android.os.RemoteException;
+import android.provider.Telephony;
+import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
+import android.test.mock.MockContentResolver;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.util.Pair;
 
 import com.android.internal.telephony.IVoidConsumer;
@@ -82,6 +86,7 @@
     @Mock private DatagramController mMockDatagramController;
     @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
     @Mock private ControllerMetricsStats mMockControllerMetricsStats;
+    @Mock private SatelliteSessionController mMockSatelliteSessionController;
 
     /** Variables required to receive datagrams in the unit tests. */
     LinkedBlockingQueue<Integer> mResultListener;
@@ -110,6 +115,8 @@
                 mMockSatelliteModemInterface);
         replaceInstance(ControllerMetricsStats.class, "sInstance", null,
                 mMockControllerMetricsStats);
+        replaceInstance(SatelliteSessionController.class, "sInstance", null,
+                mMockSatelliteSessionController);
 
         mDatagramReceiverUT = DatagramReceiver.make(mContext, Looper.myLooper(),
                 mMockDatagramController);
@@ -124,6 +131,7 @@
 
         when(mMockDatagramController.isSendingInIdleState()).thenReturn(true);
         when(mMockDatagramController.isPollingInIdleState()).thenReturn(true);
+        when(mMockDatagramController.needsWaitingForSatelliteConnected()).thenReturn(false);
         processAllMessages();
     }
 
@@ -152,17 +160,57 @@
                     .sendToTarget();
             return null;
         }).when(mMockSatelliteModemInterface).pollPendingSatelliteDatagrams(any(Message.class));
+        doReturn(true).when(mMockDatagramController).needsWaitingForSatelliteConnected();
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState())
+                .thenReturn(DatagramController.DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT);
+        mResultListener.clear();
 
         mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        assertTrue(mDatagramReceiverUT.isDatagramWaitForConnectedStateTimerStarted());
 
+        doReturn(false).when(mMockDatagramController).needsWaitingForSatelliteConnected();
+        mDatagramReceiverUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
         processAllMessages();
 
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
+        verify(mMockSatelliteModemInterface, times(1))
+                .pollPendingSatelliteDatagrams(any(Message.class));
+        assertEquals(1, mResultListener.size());
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
+        assertFalse(mDatagramReceiverUT.isDatagramWaitForConnectedStateTimerStarted());
 
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+        clearInvocations(mMockSatelliteModemInterface);
+        mResultListener.clear();
+        doReturn(true).when(mMockDatagramController).needsWaitingForSatelliteConnected();
+        mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        assertTrue(mDatagramReceiverUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        moveTimeForward(DatagramController.DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT);
+        processAllMessages();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        assertEquals(1, mResultListener.size());
+        assertThat(mResultListener.peek()).isEqualTo(
+                SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE);
+        assertFalse(mDatagramReceiverUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        mResultListener.clear();
+        mDatagramReceiverUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        assertEquals(0, mResultListener.size());
     }
 
     @Test
@@ -175,7 +223,7 @@
             mDatagramReceiverUT.obtainMessage(2 /*EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE*/,
                             new AsyncResult(message.obj, null,
                                     new SatelliteManager.SatelliteException(
-                                            SatelliteManager.SATELLITE_SERVICE_ERROR)))
+                                            SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR)))
                     .sendToTarget();
             return null;
         }).when(mMockSatelliteModemInterface).pollPendingSatelliteDatagrams(any(Message.class));
@@ -187,13 +235,14 @@
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
-                        eq(0), eq(SatelliteManager.SATELLITE_SERVICE_ERROR));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR));
 
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_SERVICE_ERROR);
+        assertThat(mResultListener.peek()).isEqualTo(
+                SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
     }
 
     @Test
@@ -209,18 +258,18 @@
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
-                        eq(0), eq(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE));
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
-                        eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
 
         assertThat(mResultListener.peek())
-                .isEqualTo(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                .isEqualTo(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
     }
 
     @Test
@@ -244,9 +293,9 @@
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
 
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
     }
 
     @Test
@@ -260,7 +309,7 @@
             mDatagramReceiverUT.obtainMessage(2 /*EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE*/,
                             new AsyncResult(message.obj, null,
                                     new SatelliteManager.SatelliteException(
-                                            SatelliteManager.SATELLITE_SERVICE_ERROR)))
+                                            SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR)))
                     .sendToTarget();
             return null;
         }).when(mPhone).pollPendingSatelliteDatagrams(any(Message.class));
@@ -272,13 +321,14 @@
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
-                        eq(0), eq(SatelliteManager.SATELLITE_SERVICE_ERROR));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR));
 
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_SERVICE_ERROR);
+        assertThat(mResultListener.peek()).isEqualTo(
+                SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
     }
 
     @Test
@@ -292,11 +342,11 @@
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE),
-                        eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
-                        eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
     }
 
     @Test
@@ -310,11 +360,11 @@
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS),
-                        eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
-                        eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
     }
 
     @Test
@@ -328,14 +378,14 @@
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS),
-                        eq(10), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(10), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
     }
 
     @Test
     public void testPollPendingSatelliteDatagrams_DemoMode_Align_succeed() throws Exception {
         // Checks invalid case only as SatelliteController does not exist in unit test
         mTestDemoModeDatagramReceiver.setDemoMode(true);
-        mTestDemoModeDatagramReceiver.onDeviceAlignedWithSatellite(true);
+        mTestDemoModeDatagramReceiver.setDeviceAlignedWithSatellite(true);
         when(mMockDatagramController.getDemoModeDatagram()).thenReturn(mDatagram);
         mTestDemoModeDatagramReceiver.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
         processAllMessages();
@@ -344,19 +394,19 @@
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING),
                         anyInt(),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
                         anyInt(),
-                        eq(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE));
+                        eq(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE));
         verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
                         anyInt(),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         assertThat(mResultListener.peek())
-                .isEqualTo(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                .isEqualTo(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
     }
 
     @Test
@@ -365,7 +415,7 @@
         long previousTimer = mTestDemoModeDatagramReceiver.getSatelliteAlignedTimeoutDuration();
         mTestDemoModeDatagramReceiver.setDemoMode(true);
         mTestDemoModeDatagramReceiver.setDuration(TEST_EXPIRE_TIMER_SATELLITE_ALIGN);
-        mTestDemoModeDatagramReceiver.onDeviceAlignedWithSatellite(false);
+        mTestDemoModeDatagramReceiver.setDeviceAlignedWithSatellite(false);
         when(mMockDatagramController.getDemoModeDatagram()).thenReturn(mDatagram);
         mTestDemoModeDatagramReceiver.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
         processAllMessages();
@@ -374,23 +424,23 @@
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING),
                         anyInt(),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         processAllFutureMessages();
         verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
                         anyInt(),
-                        eq(SatelliteManager.SATELLITE_NOT_REACHABLE));
+                        eq(SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE));
         verify(mMockDatagramController)
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
                         anyInt(),
-                        eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
         assertThat(mResultListener.peek())
-                .isEqualTo(SatelliteManager.SATELLITE_NOT_REACHABLE);
+                .isEqualTo(SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE);
 
         mTestDemoModeDatagramReceiver.setDemoMode(false);
-        mTestDemoModeDatagramReceiver.onDeviceAlignedWithSatellite(false);
+        mTestDemoModeDatagramReceiver.setDeviceAlignedWithSatellite(false);
         mTestDemoModeDatagramReceiver.setDuration(previousTimer);
     }
 
@@ -400,7 +450,7 @@
 
         mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
         processAllMessages();
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_MODEM_BUSY);
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_MODEM_BUSY);
     }
 
     @Test
@@ -410,7 +460,7 @@
 
         mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
         processAllMessages();
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_MODEM_BUSY);
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_MODEM_BUSY);
     }
 
     @Test
@@ -434,11 +484,11 @@
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(anyInt(),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED),
-                        eq(10), eq(SatelliteManager.SATELLITE_REQUEST_ABORTED));
+                        eq(10), eq(SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED));
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(anyInt(),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
-                        eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
     }
 
     @Test
@@ -453,7 +503,7 @@
         mInOrder.verify(mMockDatagramController)
                 .updateReceiveStatus(anyInt(),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
-                        eq(0), eq(SatelliteManager.SATELLITE_ERROR_NONE));
+                        eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
     }
 
     @Test
@@ -474,7 +524,7 @@
         };
 
         assertThat(mDatagramReceiverUT.registerForSatelliteDatagram(SUB_ID, callback))
-                .isEqualTo(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+                .isEqualTo(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED);
     }
 
     private static class TestDatagramReceiver extends DatagramReceiver {
@@ -491,8 +541,8 @@
         }
 
         @Override
-        protected void onDeviceAlignedWithSatellite(boolean isAligned) {
-            super.onDeviceAlignedWithSatellite(isAligned);
+        protected void setDeviceAlignedWithSatellite(boolean isAligned) {
+            super.setDeviceAlignedWithSatellite(isAligned);
         }
 
         @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
index c202f0c..0944c6c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
@@ -28,6 +28,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 
 import android.annotation.NonNull;
@@ -75,7 +76,7 @@
         replaceInstance(SatelliteController.class, "sInstance", null,
                 mMockSatelliteController);
         doReturn(Arrays.asList(SATELLITE_PLMN_ARRAY))
-                .when(mMockSatelliteController).getSatellitePlmnList();
+                .when(mMockSatelliteController).getSatellitePlmnList(anyInt());
         doReturn(mSatelliteSupportedServiceList).when(mMockSatelliteController)
                 .getSupportedSatelliteServices(SUB_ID, SATELLITE_PLMN);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
index 4587b7c..96dcd85 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
@@ -16,10 +16,7 @@
 
 package com.android.internal.telephony.satellite;
 
-import android.os.Bundle;
-import android.os.Handler;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -27,20 +24,27 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
 import android.os.Message;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.SatelliteManager.SatelliteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.util.Log;
 
 import com.android.internal.R;
@@ -75,9 +79,11 @@
 
     private PointingAppController mPointingAppController;
     InOrder mInOrder;
+    InOrder mInOrderForPointingUi;
 
     @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
     @Mock private SatelliteController mMockSatelliteController;
+    @Mock private PackageManager mPackageManager;
 
     private TestSatelliteTransmissionUpdateCallback mSatelliteTransmissionUpdateCallback;
     private TestSatelliteControllerHandler mTestSatelliteControllerHandler;
@@ -92,7 +98,7 @@
         super.setUp(getClass().getSimpleName());
         MockitoAnnotations.initMocks(this);
         logd(TAG + " Setup!");
-
+        mInOrderForPointingUi = inOrder(mContext);
         replaceInstance(SatelliteModemInterface.class, "sInstance", null,
                 mMockSatelliteModemInterface);
         replaceInstance(SatelliteController.class, "sInstance", null,
@@ -111,7 +117,7 @@
     public void tearDown() throws Exception {
         logd(TAG + " tearDown");
         mResultListener = null;
-
+        mInOrderForPointingUi = null;
         mSatelliteTransmissionUpdateCallback = null;
         super.tearDown();
     }
@@ -237,8 +243,8 @@
     }
 
     private void setUpResponseForStartTransmissionUpdates(
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SatelliteManager.SATELLITE_ERROR_NONE)
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SatelliteManager.SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -256,8 +262,8 @@
     }
 
     private void setUpResponseForStopTransmissionUpdates(
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SatelliteManager.SATELLITE_ERROR_NONE)
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SatelliteManager.SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -280,7 +286,7 @@
         doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         Message testMessage = mTestSatelliteControllerHandler
                 .obtainMessage(EVENT_START_SATELLITE_TRANSMISSION_UPDATES_DONE, null);
-        setUpResponseForStartTransmissionUpdates(SatelliteManager.SATELLITE_ERROR_NONE);
+        setUpResponseForStartTransmissionUpdates(SatelliteManager.SATELLITE_RESULT_SUCCESS);
         mPointingAppController.startSatelliteTransmissionUpdates(testMessage, mPhone);
 
         processAllMessages();
@@ -291,7 +297,7 @@
         verify(mPhone)
                 .startSatellitePositionUpdates(eq(testMessage));
 
-        assertEquals(SatelliteManager.SATELLITE_ERROR_NONE, mResultCode);
+        assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, mResultCode);
 
         assertTrue(mPointingAppController.getStartedSatelliteTransmissionUpdates());
     }
@@ -303,7 +309,7 @@
         mPointingAppController.setStartedSatelliteTransmissionUpdates(false);
         Message testMessage = mTestSatelliteControllerHandler
                 .obtainMessage(EVENT_START_SATELLITE_TRANSMISSION_UPDATES_DONE, null);
-        setUpResponseForStartTransmissionUpdates(SatelliteManager.SATELLITE_ERROR_NONE);
+        setUpResponseForStartTransmissionUpdates(SatelliteManager.SATELLITE_RESULT_SUCCESS);
         mPointingAppController.startSatelliteTransmissionUpdates(testMessage, mPhone);
 
         verify(mMockSatelliteModemInterface)
@@ -316,7 +322,7 @@
 
 
         assertTrue(mPointingAppController.getStartedSatelliteTransmissionUpdates());
-        assertEquals(SatelliteManager.SATELLITE_ERROR_NONE, mResultCode);
+        assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, mResultCode);
     }
 
     @Test
@@ -337,14 +343,14 @@
 
         assertFalse(mPointingAppController.getStartedSatelliteTransmissionUpdates());
 
-        assertEquals(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, mResultCode);
+        assertEquals(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mResultCode);
     }
 
     @Test
     public void testStopSatelliteTransmissionUpdates_CommandInterface()
             throws Exception {
         doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
-        setUpResponseForStopTransmissionUpdates(SatelliteManager.SATELLITE_ERROR_NONE);
+        setUpResponseForStopTransmissionUpdates(SatelliteManager.SATELLITE_RESULT_SUCCESS);
         Message testMessage = mTestSatelliteControllerHandler
                 .obtainMessage(EVENT_STOP_SATELLITE_TRANSMISSION_UPDATES_DONE, null);
         mPointingAppController.stopSatelliteTransmissionUpdates(testMessage, mPhone);
@@ -359,14 +365,14 @@
 
         assertFalse(mPointingAppController.getStartedSatelliteTransmissionUpdates());
 
-        assertEquals(SatelliteManager.SATELLITE_ERROR_NONE, mResultCode);
+        assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, mResultCode);
     }
 
     @Test
     public void testStopSatelliteTransmissionUpdates_success()
             throws Exception {
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
-        setUpResponseForStopTransmissionUpdates(SatelliteManager.SATELLITE_ERROR_NONE);
+        setUpResponseForStopTransmissionUpdates(SatelliteManager.SATELLITE_RESULT_SUCCESS);
         Message testMessage = mTestSatelliteControllerHandler
                 .obtainMessage(EVENT_STOP_SATELLITE_TRANSMISSION_UPDATES_DONE, null);
         mPointingAppController.stopSatelliteTransmissionUpdates(testMessage, mPhone);
@@ -380,7 +386,7 @@
                 .stopSatellitePositionUpdates(eq(testMessage));
 
         assertFalse(mPointingAppController.getStartedSatelliteTransmissionUpdates());
-        assertEquals(SatelliteManager.SATELLITE_ERROR_NONE, mResultCode);
+        assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, mResultCode);
     }
 
     @Test
@@ -399,7 +405,7 @@
         verify(mPhone, never())
                 .stopSatellitePositionUpdates(eq(testMessage));
 
-        assertEquals(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, mResultCode);
+        assertEquals(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mResultCode);
 
     }
 
@@ -417,17 +423,43 @@
     }
 
     @Test
+    public void testRestartPointingUi() throws Exception {
+        mPointingAppController.startPointingUI(true);
+        mInOrderForPointingUi.verify(mContext).startActivity(any(Intent.class));
+        testRestartPointingUi(true);
+        mPointingAppController.startPointingUI(false);
+        mInOrderForPointingUi.verify(mContext).startActivity(any(Intent.class));
+        testRestartPointingUi(false);
+    }
+
+    private void testRestartPointingUi(boolean expectedFullScreen) {
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        doReturn(new String[]{KEY_POINTING_UI_PACKAGE_NAME}).when(mPackageManager)
+            .getPackagesForUid(anyInt());
+        mPointingAppController.mUidImportanceListener.onUidImportance(1, IMPORTANCE_GONE);
+        ArgumentCaptor<Intent> restartedIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        mInOrderForPointingUi.verify(mContext).startActivity(restartedIntentCaptor.capture());
+        Intent restartIntent = restartedIntentCaptor.getValue();
+        assertEquals(KEY_POINTING_UI_PACKAGE_NAME, restartIntent.getComponent().getPackageName());
+        assertEquals(KEY_POINTING_UI_CLASS_NAME, restartIntent.getComponent().getClassName());
+        Bundle b = restartIntent.getExtras();
+        assertTrue(b.containsKey(KEY_NEED_FULL_SCREEN));
+        // Checking if last value of KEY_NEED_FULL_SCREEN is taken or not
+        assertEquals(expectedFullScreen, b.getBoolean(KEY_NEED_FULL_SCREEN));
+    }
+
+    @Test
     public void testUpdateSendDatagramTransferState() throws Exception {
         mPointingAppController.registerForSatelliteTransmissionUpdates(SUB_ID,
                 mSatelliteTransmissionUpdateCallback, mPhone);
         mPointingAppController.updateSendDatagramTransferState(SUB_ID,
                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS, 1,
-                SatelliteManager.SATELLITE_ERROR_NONE);
+                SatelliteManager.SATELLITE_RESULT_SUCCESS);
         assertTrue(waitForSendDatagramStateChangedRessult(1));
         assertEquals(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
                 mSatelliteTransmissionUpdateCallback.getState());
         assertEquals(1, mSatelliteTransmissionUpdateCallback.getSendPendingCount());
-        assertEquals(SatelliteManager.SATELLITE_ERROR_NONE,
+        assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS,
                 mSatelliteTransmissionUpdateCallback.getErrorCode());
         assertTrue(mSatelliteTransmissionUpdateCallback.inSendDatagramStateCallback);
         mPointingAppController.unregisterForSatelliteTransmissionUpdates(SUB_ID,
@@ -441,12 +473,12 @@
                 mSatelliteTransmissionUpdateCallback, mPhone);
         mPointingAppController.updateReceiveDatagramTransferState(SUB_ID,
                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS, 2,
-                SatelliteManager.SATELLITE_ERROR_NONE);
+                SatelliteManager.SATELLITE_RESULT_SUCCESS);
         assertTrue(waitForReceiveDatagramStateChangedRessult(1));
         assertEquals(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS,
                 mSatelliteTransmissionUpdateCallback.getState());
         assertEquals(2, mSatelliteTransmissionUpdateCallback.getReceivePendingCount());
-        assertEquals(SatelliteManager.SATELLITE_ERROR_NONE,
+        assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS,
                 mSatelliteTransmissionUpdateCallback.getErrorCode());
         assertTrue(mSatelliteTransmissionUpdateCallback.inReceiveDatagramStateCallback);
         mPointingAppController.unregisterForSatelliteTransmissionUpdates(SUB_ID,
@@ -484,7 +516,7 @@
                 mResultListener::offer, callback1, mPhone);
         processAllMessages();
         //since there are 2 callbacks registered for this sub_id, Handler is not unregistered
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
         mResultListener.remove();
         mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId1,
                 mResultListener::offer, callback2, mPhone);
@@ -492,7 +524,7 @@
         mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId2,
                 mResultListener::offer, callback1, mPhone);
         processAllMessages();
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
         mResultListener.remove();
         mInOrder.verify(mPhone, never()).unregisterForSatellitePositionInfoChanged(
                 any(Handler.class));
@@ -500,7 +532,7 @@
                 mResultListener::offer, callback2, null);
         processAllMessages();
         assertThat(mResultListener.peek())
-                .isEqualTo(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                .isEqualTo(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
         mResultListener.remove();
         mInOrder = null;
     }
@@ -544,7 +576,7 @@
                 mResultListener::offer, callback1, mPhone);
         processAllMessages();
         //since there are 2 callbacks registered for this sub_id, Handler is not unregistered
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
         mResultListener.remove();
         mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId1,
                 mResultListener::offer, callback2, mPhone);
@@ -555,7 +587,7 @@
         mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId2,
                 mResultListener::offer, callback1, mPhone);
         processAllMessages();
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
         mResultListener.remove();
         mInOrder.verify(mMockSatelliteModemInterface, never())
                 .unregisterForSatellitePositionInfoChanged(any(Handler.class));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
index edfd610..d9cf43c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -24,22 +24,27 @@
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_PROVISIONED;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_SUPPORTED;
 import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_EMTC_NTN;
+import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN;
 import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_NR_NTN;
 import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_PROPRIETARY;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_ERROR;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_ERROR_NONE;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_ARGUMENTS;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_MODEM_STATE;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_NOT_AUTHORIZED;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_NOT_SUPPORTED;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_NO_RESOURCES;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_REQUEST_IN_PROGRESS;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ERROR;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_ERROR;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_AUTHORIZED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NO_RESOURCES;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_MODE_ENABLED_FALSE;
 import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_MODE_ENABLED_TRUE;
@@ -52,6 +57,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.anyVararg;
 import static org.mockito.ArgumentMatchers.eq;
@@ -60,6 +66,7 @@
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -95,6 +102,7 @@
 import com.android.internal.telephony.IVoidConsumer;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
@@ -123,8 +131,6 @@
 public class SatelliteControllerTest extends TelephonyTest {
     private static final String TAG = "SatelliteControllerTest";
 
-    private static final int EVENT_DEVICE_CONFIG_CHANGED = 29;
-
     private static final long TIMEOUT = 500;
     private static final int SUB_ID = 0;
     private static final int SUB_ID1 = 1;
@@ -133,9 +139,12 @@
     private static final String TEST_NEXT_SATELLITE_TOKEN = "TEST_NEXT_SATELLITE_TOKEN";
     private static final String[] EMPTY_SATELLITE_SERVICES_SUPPORTED_BY_PROVIDERS_STRING_ARRAY = {};
     private static final int[] ACTIVE_SUB_IDS = {SUB_ID};
+    private List<Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener>>
+            mCarrierConfigChangedListenerList = new ArrayList<>();
 
     private TestSatelliteController mSatelliteControllerUT;
     private TestSharedPreferences mSharedPreferences;
+    private PersistableBundle mCarrierConfigBundle;
 
     @Mock private DatagramController mMockDatagramController;
     @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
@@ -148,6 +157,7 @@
     private List<Integer> mIIntegerConsumerResults =  new ArrayList<>();
     @Mock private ISatelliteTransmissionUpdateCallback mStartTransmissionUpdateCallback;
     @Mock private ISatelliteTransmissionUpdateCallback mStopTransmissionUpdateCallback;
+    @Mock private FeatureFlags mFeatureFlags;
     private Semaphore mIIntegerConsumerSemaphore = new Semaphore(0);
     private IIntegerConsumer mIIntegerConsumer = new IIntegerConsumer.Stub() {
         @Override
@@ -167,18 +177,22 @@
     private Set<Integer> mSupportedRadioTechnologies = new HashSet<>(Arrays.asList(
             NT_RADIO_TECHNOLOGY_NR_NTN,
             NT_RADIO_TECHNOLOGY_EMTC_NTN,
+            NT_RADIO_TECHNOLOGY_NB_IOT_NTN,
             NT_RADIO_TECHNOLOGY_PROPRIETARY));
     private SatelliteCapabilities mSatelliteCapabilities = new SatelliteCapabilities(
             mSupportedRadioTechnologies, mIsPointingRequired, MAX_BYTES_PER_OUT_GOING_DATAGRAM,
             new HashMap<>());
+    private SatelliteCapabilities mEmptySatelliteCapabilities = new SatelliteCapabilities(
+            new HashSet<>(), mIsPointingRequired, MAX_BYTES_PER_OUT_GOING_DATAGRAM,
+            new HashMap<>());
     private Semaphore mSatelliteCapabilitiesSemaphore = new Semaphore(0);
     private SatelliteCapabilities mQueriedSatelliteCapabilities = null;
-    private int mQueriedSatelliteCapabilitiesResultCode = SATELLITE_ERROR_NONE;
+    private int mQueriedSatelliteCapabilitiesResultCode = SATELLITE_RESULT_SUCCESS;
     private ResultReceiver mSatelliteCapabilitiesReceiver = new ResultReceiver(null) {
         @Override
         protected void onReceiveResult(int resultCode, Bundle resultData) {
             mQueriedSatelliteCapabilitiesResultCode = resultCode;
-            if (resultCode == SATELLITE_ERROR_NONE) {
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
                 if (resultData.containsKey(KEY_SATELLITE_CAPABILITIES)) {
                     mQueriedSatelliteCapabilities = resultData.getParcelable(
                             KEY_SATELLITE_CAPABILITIES, SatelliteCapabilities.class);
@@ -199,13 +213,13 @@
     };
 
     private boolean mQueriedSatelliteSupported = false;
-    private int mQueriedSatelliteSupportedResultCode = SATELLITE_ERROR_NONE;
+    private int mQueriedSatelliteSupportedResultCode = SATELLITE_RESULT_SUCCESS;
     private Semaphore mSatelliteSupportSemaphore = new Semaphore(0);
     private ResultReceiver mSatelliteSupportReceiver = new ResultReceiver(null) {
         @Override
         protected void onReceiveResult(int resultCode, Bundle resultData) {
             mQueriedSatelliteSupportedResultCode = resultCode;
-            if (resultCode == SATELLITE_ERROR_NONE) {
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
                 if (resultData.containsKey(KEY_SATELLITE_SUPPORTED)) {
                     mQueriedSatelliteSupported = resultData.getBoolean(KEY_SATELLITE_SUPPORTED);
                 } else {
@@ -225,14 +239,14 @@
     };
 
     private boolean mQueriedIsSatelliteEnabled = false;
-    private int mQueriedIsSatelliteEnabledResultCode = SATELLITE_ERROR_NONE;
+    private int mQueriedIsSatelliteEnabledResultCode = SATELLITE_RESULT_SUCCESS;
     private Semaphore mIsSatelliteEnabledSemaphore = new Semaphore(0);
     private ResultReceiver mIsSatelliteEnabledReceiver = new ResultReceiver(null) {
         @Override
         protected void onReceiveResult(int resultCode, Bundle resultData) {
             logd("mIsSatelliteEnabledReceiver: resultCode=" + resultCode);
             mQueriedIsSatelliteEnabledResultCode = resultCode;
-            if (resultCode == SATELLITE_ERROR_NONE) {
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
                 if (resultData.containsKey(KEY_SATELLITE_ENABLED)) {
                     mQueriedIsSatelliteEnabled = resultData.getBoolean(KEY_SATELLITE_ENABLED);
                 } else {
@@ -251,13 +265,13 @@
     };
 
     private boolean mQueriedIsDemoModeEnabled = false;
-    private int mQueriedIsDemoModeEnabledResultCode = SATELLITE_ERROR_NONE;
+    private int mQueriedIsDemoModeEnabledResultCode = SATELLITE_RESULT_SUCCESS;
     private Semaphore mIsDemoModeEnabledSemaphore = new Semaphore(0);
     private ResultReceiver mIsDemoModeEnabledReceiver = new ResultReceiver(null) {
         @Override
         protected void onReceiveResult(int resultCode, Bundle resultData) {
             mQueriedIsDemoModeEnabledResultCode = resultCode;
-            if (resultCode == SATELLITE_ERROR_NONE) {
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
                 if (resultData.containsKey(KEY_DEMO_MODE_ENABLED)) {
                     mQueriedIsDemoModeEnabled = resultData.getBoolean(KEY_DEMO_MODE_ENABLED);
                 } else {
@@ -277,13 +291,13 @@
     };
 
     private boolean mQueriedIsSatelliteProvisioned = false;
-    private int mQueriedIsSatelliteProvisionedResultCode = SATELLITE_ERROR_NONE;
+    private int mQueriedIsSatelliteProvisionedResultCode = SATELLITE_RESULT_SUCCESS;
     private Semaphore mIsSatelliteProvisionedSemaphore = new Semaphore(0);
     private ResultReceiver mIsSatelliteProvisionedReceiver = new ResultReceiver(null) {
         @Override
         protected void onReceiveResult(int resultCode, Bundle resultData) {
             mQueriedIsSatelliteProvisionedResultCode = resultCode;
-            if (resultCode == SATELLITE_ERROR_NONE) {
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
                 if (resultData.containsKey(KEY_SATELLITE_PROVISIONED)) {
                     mQueriedIsSatelliteProvisioned =
                             resultData.getBoolean(KEY_SATELLITE_PROVISIONED);
@@ -304,13 +318,13 @@
     };
 
     private boolean mQueriedSatelliteAllowed = false;
-    private int mQueriedSatelliteAllowedResultCode = SATELLITE_ERROR_NONE;
+    private int mQueriedSatelliteAllowedResultCode = SATELLITE_RESULT_SUCCESS;
     private Semaphore mSatelliteAllowedSemaphore = new Semaphore(0);
     private ResultReceiver mSatelliteAllowedReceiver = new ResultReceiver(null) {
         @Override
         protected void onReceiveResult(int resultCode, Bundle resultData) {
             mQueriedSatelliteAllowedResultCode = resultCode;
-            if (resultCode == SATELLITE_ERROR_NONE) {
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
                 if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
                     mQueriedSatelliteAllowed = resultData.getBoolean(
                             KEY_SATELLITE_COMMUNICATION_ALLOWED);
@@ -332,13 +346,13 @@
 
     private int mQueriedSatelliteVisibilityTime = -1;
     private int mSatelliteNextVisibilityTime = 3600;
-    private int mQueriedSatelliteVisibilityTimeResultCode = SATELLITE_ERROR_NONE;
+    private int mQueriedSatelliteVisibilityTimeResultCode = SATELLITE_RESULT_SUCCESS;
     private Semaphore mSatelliteVisibilityTimeSemaphore = new Semaphore(0);
     private ResultReceiver mSatelliteVisibilityTimeReceiver = new ResultReceiver(null) {
         @Override
         protected void onReceiveResult(int resultCode, Bundle resultData) {
             mQueriedSatelliteVisibilityTimeResultCode = resultCode;
-            if (resultCode == SATELLITE_ERROR_NONE) {
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
                 if (resultData.containsKey(KEY_SATELLITE_NEXT_VISIBILITY)) {
                     mQueriedSatelliteVisibilityTime = resultData.getInt(
                             KEY_SATELLITE_NEXT_VISIBILITY);
@@ -358,10 +372,6 @@
         }
     };
 
-    private List<Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener>>
-            mCarrierConfigChangedListenerList = new ArrayList<>();
-    private PersistableBundle mCarrierConfigBundle;
-
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
@@ -407,11 +417,13 @@
         doReturn(mIsSatelliteServiceSupported)
                 .when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         setUpResponseForRequestSatelliteCapabilities(
-                mSatelliteCapabilities, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RADIO_NOT_AVAILABLE);
+                mSatelliteCapabilities, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteSupported(false,
+                SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         doNothing().when(mMockDatagramController).setDemoMode(anyBoolean());
         doNothing().when(mMockSatelliteSessionController)
                 .onSatelliteEnabledStateChanged(anyBoolean());
+        doNothing().when(mMockSatelliteSessionController).onSatelliteModemStateChanged(anyInt());
         doNothing().when(mMockSatelliteSessionController).setDemoMode(anyBoolean());
         doNothing().when(mMockControllerMetricsStats).onSatelliteEnabled();
         doNothing().when(mMockControllerMetricsStats).reportServiceEnablementSuccessCount();
@@ -428,7 +440,9 @@
                     .setIsProvisionRequest(eq(false));
         doNothing().when(mMockProvisionMetricsStats).reportProvisionMetrics();
         doNothing().when(mMockControllerMetricsStats).reportDeprovisionCount(anyInt());
-        mSatelliteControllerUT = new TestSatelliteController(mContext, Looper.myLooper());
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        mSatelliteControllerUT =
+                new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
         verify(mMockSatelliteModemInterface).registerForSatelliteProvisionStateChanged(
                 any(Handler.class),
                 eq(26) /* EVENT_SATELLITE_PROVISION_STATE_CHANGED */,
@@ -453,135 +467,141 @@
     @Test
     public void testRequestIsSatelliteCommunicationAllowedForCurrentLocation() {
         mSatelliteAllowedSemaphore.drainPermits();
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestIsSatelliteCommunicationAllowedForCurrentLocation(SUB_ID,
                 mSatelliteAllowedReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(1));
-        assertEquals(SATELLITE_NOT_SUPPORTED, mQueriedSatelliteAllowedResultCode);
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, mQueriedSatelliteAllowedResultCode);
 
         resetSatelliteControllerUT();
         mSatelliteControllerUT.requestIsSatelliteCommunicationAllowedForCurrentLocation(SUB_ID,
                 mSatelliteAllowedReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteAllowedResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mQueriedSatelliteAllowedResultCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteAllowedForCurrentLocation(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteAllowedForCurrentLocation(true,
+                SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestIsSatelliteCommunicationAllowedForCurrentLocation(SUB_ID,
                 mSatelliteAllowedReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, mQueriedSatelliteAllowedResultCode);
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode);
         assertTrue(mQueriedSatelliteAllowed);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpNullResponseForRequestIsSatelliteAllowedForCurrentLocation(SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpNullResponseForRequestIsSatelliteAllowedForCurrentLocation(SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestIsSatelliteCommunicationAllowedForCurrentLocation(SUB_ID,
                 mSatelliteAllowedReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteAllowedResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mQueriedSatelliteAllowedResultCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpNullResponseForRequestIsSatelliteAllowedForCurrentLocation(
-                SATELLITE_INVALID_MODEM_STATE);
+                SATELLITE_RESULT_INVALID_MODEM_STATE);
         mSatelliteControllerUT.requestIsSatelliteCommunicationAllowedForCurrentLocation(SUB_ID,
                 mSatelliteAllowedReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(1));
-        assertEquals(SATELLITE_INVALID_MODEM_STATE, mQueriedSatelliteAllowedResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, mQueriedSatelliteAllowedResultCode);
     }
 
     @Test
     public void testRequestTimeForNextSatelliteVisibility() {
         mSatelliteVisibilityTimeSemaphore.drainPermits();
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
-        assertEquals(SATELLITE_NOT_SUPPORTED, mQueriedSatelliteVisibilityTimeResultCode);
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, mQueriedSatelliteVisibilityTimeResultCode);
 
         resetSatelliteControllerUT();
         mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteVisibilityTimeResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                mQueriedSatelliteVisibilityTimeResultCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestTimeForNextSatelliteVisibility(mSatelliteNextVisibilityTime,
-                SATELLITE_ERROR_NONE);
+                SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteVisibilityTimeResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                mQueriedSatelliteVisibilityTimeResultCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestTimeForNextSatelliteVisibility(mSatelliteNextVisibilityTime,
-                SATELLITE_ERROR_NONE);
+                SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
-        assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, mQueriedSatelliteVisibilityTimeResultCode);
+        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
+                mQueriedSatelliteVisibilityTimeResultCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestTimeForNextSatelliteVisibility(mSatelliteNextVisibilityTime,
-                SATELLITE_ERROR_NONE);
+                SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, mQueriedSatelliteVisibilityTimeResultCode);
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteVisibilityTimeResultCode);
         assertEquals(mSatelliteNextVisibilityTime, mQueriedSatelliteVisibilityTime);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         setUpNullResponseForRequestTimeForNextSatelliteVisibility(
-                SATELLITE_ERROR_NONE);
+                SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteVisibilityTimeResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                mQueriedSatelliteVisibilityTimeResultCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         setUpNullResponseForRequestTimeForNextSatelliteVisibility(
-                SATELLITE_INVALID_MODEM_STATE);
+                SATELLITE_RESULT_INVALID_MODEM_STATE);
         mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
-        assertEquals(SATELLITE_INVALID_MODEM_STATE, mQueriedSatelliteVisibilityTimeResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE,
+                mQueriedSatelliteVisibilityTimeResultCode);
     }
 
     @Test
@@ -593,16 +613,17 @@
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
 
         // Fail to enable satellite when the device does not support satellite.
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
 
         // Fail to enable satellite when the device is not provisioned yet.
         mIIntegerConsumerResults.clear();
@@ -610,28 +631,29 @@
         verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(false));
         verify(mMockSatelliteSessionController, times(1)).setDemoMode(eq(false));
         verify(mMockDatagramController, times(1)).setDemoMode(eq(false));
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
+                (long) mIIntegerConsumerResults.get(0));
 
         sendProvisionedStateChangedEvent(true, null);
         processAllMessages();
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
 
         // Successfully enable satellite
         mIIntegerConsumerResults.clear();
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
-        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
         assertEquals(
                 SATELLITE_MODE_ENABLED_TRUE, mSatelliteControllerUT.satelliteModeSettingValue);
@@ -644,12 +666,12 @@
 
         // Successfully disable satellite when radio is turned off.
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
-        setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_RESULT_SUCCESS);
         setRadioPower(false);
         processAllMessages();
         sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_OFF, null);
         processAllMessages();
-        verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
         assertEquals(
                 SATELLITE_MODE_ENABLED_FALSE, mSatelliteControllerUT.satelliteModeSettingValue);
@@ -660,27 +682,27 @@
 
         // Fail to enable satellite when radio is off.
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         // Radio is not on, can not enable satellite
-        assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
 
         setRadioPower(true);
         processAllMessages();
-        verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
 
         // Fail to enable satellite with an error response from modem when radio is on.
         mIIntegerConsumerResults.clear();
         clearInvocations(mMockPointingAppController);
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
-        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_INVALID_MODEM_STATE);
+        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_RESULT_INVALID_MODEM_STATE);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
         verify(mMockPointingAppController, never()).startPointingUI(anyBoolean());
         assertFalse(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
         verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementFailCount();
@@ -688,12 +710,12 @@
         // Successfully enable satellite when radio is on.
         mIIntegerConsumerResults.clear();
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
-        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
         assertEquals(SATELLITE_MODE_ENABLED_TRUE, mSatelliteControllerUT.satelliteModeSettingValue);
         verify(mMockPointingAppController).startPointingUI(eq(false));
@@ -711,41 +733,41 @@
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
 
         // Fail to enable satellite with a different demo mode when it is already enabled.
         mIIntegerConsumerResults.clear();
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, true, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_ARGUMENTS, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
+        assertEquals(SATELLITE_RESULT_INVALID_ARGUMENTS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
 
         // Successfully disable satellite.
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
 
         // Disable satellite when satellite is already disabled.
         mIIntegerConsumerResults.clear();
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
 
         // Disable satellite with a different demo mode when satellite is already disabled.
         mIIntegerConsumerResults.clear();
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, true, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
 
         // Send a second request while the first request in progress
         mIIntegerConsumerResults.clear();
@@ -756,13 +778,13 @@
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_REQUEST_IN_PROGRESS, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_REQUEST_IN_PROGRESS, (long) mIIntegerConsumerResults.get(0));
 
         mIIntegerConsumerResults.clear();
         resetSatelliteControllerUTToSupportedAndProvisionedState();
         // Should receive callback for the above request when satellite modem is turned off.
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
 
         // Move to satellite-disabling in progress.
         setUpNoResponseForRequestSatelliteEnabled(false, false);
@@ -775,52 +797,52 @@
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_ERROR, (long) mIIntegerConsumerResults.get(0));
 
         mIIntegerConsumerResults.clear();
         resetSatelliteControllerUTToOffAndProvisionedState();
         // Should receive callback for the above request when satellite modem is turned off.
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
 
         /**
          * Make areAllRadiosDisabled return false and move mWaitingForRadioDisabled to true, which
          * will lead to no response for requestSatelliteEnabled.
          */
         mSatelliteControllerUT.allRadiosDisabled = false;
-        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
 
         resetSatelliteControllerUTEnabledState();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
         processAllMessages();
         // We should receive 2 callbacks for the above 2 requests.
         assertTrue(waitForIIntegerConsumerResult(2));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(1));
 
         resetSatelliteControllerUTToOffAndProvisionedState();
 
         // Repeat the same test as above but with error response from modem for the second request
         mSatelliteControllerUT.allRadiosDisabled = false;
-        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
 
         resetSatelliteControllerUTEnabledState();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_NO_RESOURCES);
+        setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_RESULT_NO_RESOURCES);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
         processAllMessages();
         // We should receive 2 callbacks for the above 2 requests.
         assertTrue(waitForIIntegerConsumerResult(2));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
-        assertEquals(SATELLITE_NO_RESOURCES, (long) mIIntegerConsumerResults.get(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_NO_RESOURCES, (long) mIIntegerConsumerResults.get(1));
         mSatelliteControllerUT.allRadiosDisabled = true;
     }
 
@@ -830,42 +852,45 @@
         mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteCapabilitiesResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                mQueriedSatelliteCapabilitiesResultCode);
 
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
-        assertEquals(SATELLITE_NOT_SUPPORTED, mQueriedSatelliteCapabilitiesResultCode);
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, mQueriedSatelliteCapabilitiesResultCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestSatelliteCapabilities(mSatelliteCapabilities, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestSatelliteCapabilities(mSatelliteCapabilities,
+                SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, mQueriedSatelliteCapabilitiesResultCode);
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteCapabilitiesResultCode);
         assertEquals(mSatelliteCapabilities, mQueriedSatelliteCapabilities);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpNullResponseForRequestSatelliteCapabilities(SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpNullResponseForRequestSatelliteCapabilities(SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedSatelliteCapabilitiesResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                mQueriedSatelliteCapabilitiesResultCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpNullResponseForRequestSatelliteCapabilities(SATELLITE_INVALID_MODEM_STATE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpNullResponseForRequestSatelliteCapabilities(SATELLITE_RESULT_INVALID_MODEM_STATE);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
-        assertEquals(SATELLITE_INVALID_MODEM_STATE, mQueriedSatelliteCapabilitiesResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, mQueriedSatelliteCapabilitiesResultCode);
     }
 
     @Test
@@ -876,70 +901,74 @@
                 mStartTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
 
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
+                (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         verify(mMockPointingAppController).registerForSatelliteTransmissionUpdates(anyInt(),
                 eq(mStartTransmissionUpdateCallback), any());
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         verify(mMockPointingAppController).startSatelliteTransmissionUpdates(any(Message.class),
                 any(Phone.class));
         verify(mMockPointingAppController).setStartedSatelliteTransmissionUpdates(eq(true));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_INVALID_TELEPHONY_STATE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
         mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
         verify(mMockPointingAppController).unregisterForSatelliteTransmissionUpdates(anyInt(),
                 any(),  eq(mStartTransmissionUpdateCallback), any(Phone.class));
         verify(mMockPointingAppController).setStartedSatelliteTransmissionUpdates(eq(false));
@@ -953,69 +982,73 @@
                 mStopTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
 
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
+                (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
         verify(mMockPointingAppController).unregisterForSatelliteTransmissionUpdates(anyInt(),
                 any(),  eq(mStopTransmissionUpdateCallback), any(Phone.class));
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         verify(mMockPointingAppController).stopSatelliteTransmissionUpdates(any(Message.class),
                 any(Phone.class));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_INVALID_TELEPHONY_STATE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
         mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
     }
 
     @Test
@@ -1024,51 +1057,51 @@
         resetSatelliteControllerUT();
         mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedIsDemoModeEnabledResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mQueriedIsDemoModeEnabledResultCode);
         assertFalse(mQueriedIsDemoModeEnabled);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
-        assertEquals(SATELLITE_NOT_SUPPORTED, mQueriedIsDemoModeEnabledResultCode);
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, mQueriedIsDemoModeEnabledResultCode);
         assertFalse(mQueriedIsDemoModeEnabled);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, mQueriedIsDemoModeEnabledResultCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mQueriedIsDemoModeEnabledResultCode);
         assertFalse(mQueriedIsDemoModeEnabled);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
-        assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, mQueriedIsDemoModeEnabledResultCode);
+        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED, mQueriedIsDemoModeEnabledResultCode);
         assertFalse(mQueriedIsDemoModeEnabled);
 
         resetSatelliteControllerUT();
         boolean isDemoModeEnabled = mSatelliteControllerUT.isDemoModeEnabled();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, mQueriedIsDemoModeEnabledResultCode);
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedIsDemoModeEnabledResultCode);
         assertEquals(isDemoModeEnabled, mQueriedIsDemoModeEnabled);
     }
 
     @Test
     public void testIsSatelliteEnabled() {
         assertFalse(mSatelliteControllerUT.isSatelliteEnabled());
-        setUpResponseForRequestIsSatelliteEnabled(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
         mIsSatelliteEnabledSemaphore.drainPermits();
         mSatelliteControllerUT.requestIsSatelliteEnabled(SUB_ID, mIsSatelliteEnabledReceiver);
         processAllMessages();
@@ -1078,20 +1111,20 @@
 
     @Test
     public void testOnSatelliteServiceConnected() {
-        verifySatelliteSupported(false, SATELLITE_RADIO_NOT_AVAILABLE);
-        verifySatelliteEnabled(false, SATELLITE_INVALID_TELEPHONY_STATE);
-        verifySatelliteProvisioned(false, SATELLITE_INVALID_TELEPHONY_STATE);
+        verifySatelliteSupported(false, SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
+        verifySatelliteEnabled(false, SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
 
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_RESULT_SUCCESS);
 
         mSatelliteControllerUT.onSatelliteServiceConnected();
         processAllMessages();
 
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
     }
 
     @Test
@@ -1104,7 +1137,7 @@
         };
         int errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(
                 SUB_ID, callback);
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, errorCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
         verify(mMockSatelliteSessionController, never())
                 .registerForSatelliteModemStateChanged(callback);
 
@@ -1112,7 +1145,7 @@
 
         errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(
                 SUB_ID, callback);
-        assertEquals(SATELLITE_ERROR_NONE, errorCode);
+        assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
         verify(mMockSatelliteSessionController).registerForSatelliteModemStateChanged(callback);
     }
 
@@ -1152,20 +1185,20 @@
                 };
         int errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(
                 SUB_ID, callback);
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, errorCode);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
 
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(
                 SUB_ID, callback);
-        assertEquals(SATELLITE_NOT_SUPPORTED, errorCode);
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, errorCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(
                 SUB_ID, callback);
-        assertEquals(SATELLITE_ERROR_NONE, errorCode);
+        assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
 
         sendProvisionedStateChangedEvent(true, null);
         processAllMessages();
@@ -1191,9 +1224,9 @@
                     }
                 };
         when(mMockDatagramController.registerForSatelliteDatagram(eq(SUB_ID), eq(callback)))
-                .thenReturn(SATELLITE_ERROR_NONE);
+                .thenReturn(SATELLITE_RESULT_SUCCESS);
         int errorCode = mSatelliteControllerUT.registerForSatelliteDatagram(SUB_ID, callback);
-        assertEquals(SATELLITE_ERROR_NONE, errorCode);
+        assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
         verify(mMockDatagramController).registerForSatelliteDatagram(eq(SUB_ID), eq(callback));
     }
 
@@ -1224,22 +1257,24 @@
                 SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, datagram, true, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
         verify(mMockDatagramController, never()).sendSatelliteDatagram(anyInt(),
                 eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(datagram), eq(true),
                 any());
 
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         sendProvisionedStateChangedEvent(false, null);
         processAllMessages();
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.sendSatelliteDatagram(SUB_ID,
                 SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, datagram, true, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
+                (long) mIIntegerConsumerResults.get(0));
         verify(mMockDatagramController, never()).sendSatelliteDatagram(anyInt(),
                 eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(datagram), eq(true),
                 any());
@@ -1247,7 +1282,7 @@
         mIIntegerConsumerResults.clear();
         sendProvisionedStateChangedEvent(true, null);
         processAllMessages();
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.sendSatelliteDatagram(SUB_ID,
                 SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, datagram, true, mIIntegerConsumer);
         processAllMessages();
@@ -1264,25 +1299,27 @@
         mSatelliteControllerUT.pollPendingSatelliteDatagrams(SUB_ID, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
         verify(mMockDatagramController, never()).pollPendingSatelliteDatagrams(anyInt(), any());
 
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         sendProvisionedStateChangedEvent(false, null);
         processAllMessages();
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.pollPendingSatelliteDatagrams(SUB_ID, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_SERVICE_NOT_PROVISIONED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
+                (long) mIIntegerConsumerResults.get(0));
         verify(mMockDatagramController, never()).pollPendingSatelliteDatagrams(anyInt(), any());
 
         mIIntegerConsumerResults.clear();
         sendProvisionedStateChangedEvent(true, null);
         processAllMessages();
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.pollPendingSatelliteDatagrams(SUB_ID, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
@@ -1301,94 +1338,95 @@
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
         assertNull(cancelRemote);
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
         assertNull(cancelRemote);
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         assertNull(cancelRemote);
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         setUpResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN, testProvisionData,
-                SATELLITE_ERROR_NONE);
+                SATELLITE_RESULT_SUCCESS);
         cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         assertNotNull(cancelRemote);
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         setUpResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN, testProvisionData,
-                SATELLITE_NOT_AUTHORIZED);
+                SATELLITE_RESULT_NOT_AUTHORIZED);
         cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_NOT_AUTHORIZED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_NOT_AUTHORIZED, (long) mIIntegerConsumerResults.get(0));
         assertNotNull(cancelRemote);
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         setUpResponseForProvisionSatelliteService(TEST_NEXT_SATELLITE_TOKEN, testProvisionData,
-                SATELLITE_ERROR_NONE);
+                SATELLITE_RESULT_SUCCESS);
         cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
                 TEST_NEXT_SATELLITE_TOKEN, testProvisionData, mIIntegerConsumer);
         cancellationSignal.setRemote(cancelRemote);
         cancellationSignal.cancel();
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         verify(mMockSatelliteModemInterface).deprovisionSatelliteService(
                 eq(TEST_NEXT_SATELLITE_TOKEN), any(Message.class));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         setUpNoResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN);
         setUpResponseForProvisionSatelliteService(TEST_NEXT_SATELLITE_TOKEN, testProvisionData,
-                SATELLITE_ERROR_NONE);
+                SATELLITE_RESULT_SUCCESS);
         cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
@@ -1397,7 +1435,7 @@
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_SERVICE_PROVISION_IN_PROGRESS,
+        assertEquals(SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS,
                 (long) mIIntegerConsumerResults.get(0));
     }
 
@@ -1405,13 +1443,13 @@
     public void testDeprovisionSatelliteService() {
         mIIntegerConsumerSemaphore.drainPermits();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
@@ -1419,63 +1457,65 @@
                  TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_TELEPHONY_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+                (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(false, SATELLITE_ERROR_NONE);
-        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN,
-                SATELLITE_INVALID_MODEM_STATE);
+                SATELLITE_RESULT_INVALID_MODEM_STATE);
         mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
     }
 
     @Test
     public void testSupportedSatelliteServices() {
-        List<String> satellitePlmnList = mSatelliteControllerUT.getSatellitePlmnList();
+        List<String> satellitePlmnList = mSatelliteControllerUT.getSatellitePlmnList(SUB_ID);
         assertEquals(EMPTY_SATELLITE_SERVICES_SUPPORTED_BY_PROVIDERS_STRING_ARRAY.length,
                 satellitePlmnList.size());
         List<Integer> supportedSatelliteServices =
@@ -1491,9 +1531,9 @@
                 R.array.config_satellite_services_supported_by_providers,
                 satelliteServicesSupportedByProviderStrArray);
         TestSatelliteController testSatelliteController =
-                new TestSatelliteController(mContext, Looper.myLooper());
+                new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
 
-        satellitePlmnList = testSatelliteController.getSatellitePlmnList();
+        satellitePlmnList = testSatelliteController.getSatellitePlmnList(SUB_ID);
         assertTrue(Arrays.equals(satellitePlmnArray, satellitePlmnList.stream().toArray()));
 
         supportedSatelliteServices =
@@ -1514,11 +1554,12 @@
 
         // Carrier config changed
         int[] expectedSupportedServices3 = {2};
-        int[] supportedServices = {1, 3};
+        int[] expectedSupportedServices4 = {1, 3};
         PersistableBundle carrierSupportedSatelliteServicesPerProvider = new PersistableBundle();
         carrierSupportedSatelliteServicesPerProvider.putIntArray(
                 "00102", expectedSupportedServices3);
-        carrierSupportedSatelliteServicesPerProvider.putIntArray("00103", supportedServices);
+        carrierSupportedSatelliteServicesPerProvider.putIntArray(
+                "00103", expectedSupportedServices4);
         mCarrierConfigBundle.putPersistableBundle(CarrierConfigManager
                         .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
                 carrierSupportedSatelliteServicesPerProvider);
@@ -1548,7 +1589,10 @@
 
         supportedSatelliteServices =
                 mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00103");
-        assertTrue(supportedSatelliteServices.isEmpty());
+        assertTrue(Arrays.equals(expectedSupportedServices4,
+                supportedSatelliteServices.stream()
+                        .mapToInt(Integer::intValue)
+                        .toArray()));
 
         // Subscriptions changed
         int[] newActiveSubIds = {SUB_ID1};
@@ -1567,6 +1611,9 @@
         supportedSatelliteServices =
                 testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00102");
         assertTrue(supportedSatelliteServices.isEmpty());
+        supportedSatelliteServices =
+                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00103");
+        assertTrue(supportedSatelliteServices.isEmpty());
 
         supportedSatelliteServices =
                 testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00101");
@@ -1586,22 +1633,281 @@
 
         supportedSatelliteServices =
                 testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00103");
-        assertTrue(supportedSatelliteServices.isEmpty());
+        assertTrue(Arrays.equals(expectedSupportedServices4,
+                supportedSatelliteServices.stream()
+                        .mapToInt(Integer::intValue)
+                        .toArray()));
+    }
+
+    @Test
+    public void testConfigureSatellitePlmnOnCarrierConfigChanged() {
+        logd("testConfigureSatellitePlmnOnCarrierConfigChanged");
+
+        String[] satelliteServicesSupportedByProviderStrArray =
+                {"00101:1,2", "00102:2,3", "00103:1,2", "00104:3,4", "00105:1"};
+        mContextFixture.putStringArrayResource(
+                R.array.config_satellite_services_supported_by_providers,
+                satelliteServicesSupportedByProviderStrArray);
+
+        /* Initially, the radio state is ON. In the constructor, satelliteController registers for
+         the radio state changed events and immediately gets the radio state changed event as ON. */
+        doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        TestSatelliteController testSatelliteController =
+                new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
+        processAllMessages();
+        List<String> satellitePlmnList = testSatelliteController.getSatellitePlmnList(SUB_ID);
+        verify(mMockSatelliteModemInterface, never())
+                .setSatellitePlmn(anyInt(), eq(satellitePlmnList), any(Message.class));
+        reset(mMockSatelliteModemInterface);
+
+        // Test setSatellitePlmn() when Carrier Config change event triggered.
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(1)).setSatellitePlmn(anyInt(), anyList(), any(
+                Message.class));
+        reset(mMockSatelliteModemInterface);
+
+        /* setSatellitePlmn() is called regardless whether satellite attach for carrier is
+           supported. */
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(1)).setSatellitePlmn(anyInt(), anyList(), any(
+                Message.class));
+        reset(mMockSatelliteModemInterface);
+    }
+
+    @Test
+    public void testSatelliteCommunicationRestriction() {
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true);
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        processAllMessages();
+
+        // Remove restriction reason if exist
+        mIIntegerConsumerResults.clear();
+        reset(mMockSatelliteModemInterface);
+        setUpResponseForRequestSetSatelliteEnabledForCarrier(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+                SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
+        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+                SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(2));
+
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(1));
+
+        Set<Integer> restrictionSet =
+                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+        assertTrue(!restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER));
+        assertTrue(!restrictionSet.contains(
+                SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION));
+
+        // Add satellite attach restriction reason by user
+        mIIntegerConsumerResults.clear();
+        reset(mMockSatelliteModemInterface);
+        setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        mSatelliteControllerUT.addSatelliteAttachRestrictionForCarrier(SUB_ID,
+                SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
+        processAllMessages();
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verify(mMockSatelliteModemInterface, never())
+                .requestSetSatelliteEnabledForCarrier(anyInt(), anyBoolean(), any(Message.class));
+        assertTrue(waitForIIntegerConsumerResult(1));
+        restrictionSet =
+                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+        assertTrue(restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER));
+
+        // remove satellite restriction reason by user
+        mIIntegerConsumerResults.clear();
+        reset(mMockSatelliteModemInterface);
+        setUpResponseForRequestSetSatelliteEnabledForCarrier(true, SATELLITE_RESULT_SUCCESS);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+                SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        restrictionSet =
+                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+        assertTrue(!restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER));
+        verify(mMockSatelliteModemInterface, times(1))
+                .requestSetSatelliteEnabledForCarrier(anyInt(), anyBoolean(), any(Message.class));
+
+        // Add satellite attach restriction reason by user
+        mIIntegerConsumerResults.clear();
+        reset(mMockSatelliteModemInterface);
+        setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        mSatelliteControllerUT.addSatelliteAttachRestrictionForCarrier(SUB_ID,
+                SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        restrictionSet =
+                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+        assertTrue(restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER));
+        verify(mMockSatelliteModemInterface, times(1))
+                .requestSetSatelliteEnabledForCarrier(anyInt(), eq(false), any(Message.class));
+
+        // add satellite attach restriction reason by geolocation
+        mIIntegerConsumerResults.clear();
+        reset(mMockSatelliteModemInterface);
+        setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.addSatelliteAttachRestrictionForCarrier(SUB_ID,
+                SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION, mIIntegerConsumer);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        restrictionSet =
+                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+        assertTrue(restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION));
+        verify(mMockSatelliteModemInterface, never())
+                .requestSetSatelliteEnabledForCarrier(anyInt(), anyBoolean(), any(Message.class));
+
+        // remove satellite attach restriction reason by geolocation
+        mIIntegerConsumerResults.clear();
+        reset(mMockSatelliteModemInterface);
+        setUpResponseForRequestSetSatelliteEnabledForCarrier(true, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+                SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION, mIIntegerConsumer);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        restrictionSet =
+                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+        assertTrue(!restrictionSet.contains(
+                SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION));
+        verify(mMockSatelliteModemInterface, never())
+                .requestSetSatelliteEnabledForCarrier(anyInt(), anyBoolean(), any(Message.class));
+
+        // remove satellite restriction reason by user
+        mIIntegerConsumerResults.clear();
+        reset(mMockSatelliteModemInterface);
+        setUpResponseForRequestSetSatelliteEnabledForCarrier(true, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+                SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        restrictionSet =
+                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+        assertTrue(!restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER));
+        verify(mMockSatelliteModemInterface, times(1))
+                .requestSetSatelliteEnabledForCarrier(anyInt(), eq(true), any(Message.class));
+        reset(mMockSatelliteModemInterface);
+    }
+
+    @Test
+    public void testIsSatelliteAttachRequired() {
+        mSatelliteCapabilitiesSemaphore.drainPermits();
+        TestSatelliteController satelliteController =
+                new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
+        satelliteController.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        processAllMessages();
+        assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
+        assertEquals(
+                SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mQueriedSatelliteCapabilitiesResultCode);
+        assertFalse(satelliteController.isSatelliteAttachRequired());
+
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestSatelliteCapabilities(
+                mSatelliteCapabilities, SATELLITE_RESULT_MODEM_ERROR);
+        satelliteController =
+                new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestSatelliteCapabilities(
+                mEmptySatelliteCapabilities, SATELLITE_RESULT_SUCCESS);
+        satelliteController.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        processAllMessages();
+        assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteCapabilitiesResultCode);
+        assertEquals(mEmptySatelliteCapabilities, mQueriedSatelliteCapabilities);
+        assertFalse(satelliteController.isSatelliteAttachRequired());
+
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestSatelliteCapabilities(
+                mSatelliteCapabilities, SATELLITE_RESULT_MODEM_ERROR);
+        satelliteController =
+                new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestSatelliteCapabilities(
+                mSatelliteCapabilities, SATELLITE_RESULT_SUCCESS);
+        satelliteController.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        processAllMessages();
+        assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteCapabilitiesResultCode);
+        assertEquals(mSatelliteCapabilities, mQueriedSatelliteCapabilities);
+        assertTrue(satelliteController.isSatelliteAttachRequired());
+
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
+        assertFalse(satelliteController.isSatelliteAttachRequired());
+    }
+
+    @Test
+    public void testSatelliteModemStateChanged() {
+        clearInvocations(mMockSatelliteSessionController);
+        clearInvocations(mMockDatagramController);
+        sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_CONNECTED, null);
+        processAllMessages();
+        verify(mMockSatelliteSessionController, times(0)).onSatelliteModemStateChanged(
+                SATELLITE_MODEM_STATE_CONNECTED);
+
+        resetSatelliteControllerUTToSupportedAndProvisionedState();
+        clearInvocations(mMockSatelliteSessionController);
+        clearInvocations(mMockDatagramController);
+        sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_UNAVAILABLE, null);
+        processAllMessages();
+        verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(false));
+        verify(mMockSatelliteSessionController, times(1)).setDemoMode(eq(false));
+        verify(mMockDatagramController, times(1)).setDemoMode(eq(false));
+
+        clearInvocations(mMockSatelliteSessionController);
+        clearInvocations(mMockDatagramController);
+        sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_CONNECTED, null);
+        processAllMessages();
+        verify(mMockSatelliteSessionController, times(1)).onSatelliteModemStateChanged(
+                SATELLITE_MODEM_STATE_CONNECTED);
     }
 
     private void resetSatelliteControllerUTEnabledState() {
         logd("resetSatelliteControllerUTEnabledState");
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RADIO_NOT_AVAILABLE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         doNothing().when(mMockSatelliteModemInterface)
                 .setSatelliteServicePackageName(anyString());
         mSatelliteControllerUT.setSatelliteServicePackageName("TestSatelliteService");
         processAllMessages();
 
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         sendProvisionedStateChangedEvent(true, null);
         processAllMessages();
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
     }
 
     private void resetSatelliteControllerUT() {
@@ -1611,7 +1917,7 @@
         processAllMessages();
 
         // Reset all cached states
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RADIO_NOT_AVAILABLE);
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         doNothing().when(mMockSatelliteModemInterface)
                 .setSatelliteServicePackageName(anyString());
         mSatelliteControllerUT.setSatelliteServicePackageName("TestSatelliteService");
@@ -1620,11 +1926,11 @@
 
     private void resetSatelliteControllerUTToSupportedAndProvisionedState() {
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_ERROR_NONE);
-        verifySatelliteSupported(true, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         sendProvisionedStateChangedEvent(true, null);
         processAllMessages();
-        verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
     }
 
     private void resetSatelliteControllerUTToOffAndProvisionedState() {
@@ -1632,7 +1938,7 @@
         // Clean up pending resources and move satellite controller to OFF state.
         sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_UNAVAILABLE, null);
         processAllMessages();
-        verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
     }
 
     private void resetSatelliteControllerUTToOnAndProvisionedState() {
@@ -1640,17 +1946,17 @@
         setRadioPower(true);
         processAllMessages();
 
-        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+        setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
     }
 
     private void setUpResponseForRequestIsSatelliteEnabled(boolean isSatelliteEnabled,
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -1661,8 +1967,8 @@
     }
 
     private void setUpResponseForRequestIsSatelliteSupported(
-            boolean isSatelliteSupported, @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            boolean isSatelliteSupported, @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -1673,8 +1979,8 @@
     }
 
     private void setUpResponseForRequestIsSatelliteAllowedForCurrentLocation(
-            boolean isSatelliteAllowed, @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            boolean isSatelliteAllowed, @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -1686,8 +1992,8 @@
     }
 
     private void setUpNullResponseForRequestIsSatelliteAllowedForCurrentLocation(
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -1699,8 +2005,8 @@
     }
 
     private void setUpResponseForRequestTimeForNextSatelliteVisibility(
-            int satelliteVisibilityTime, @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            int satelliteVisibilityTime, @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         int[] visibilityTime = new int[] {satelliteVisibilityTime};
         doAnswer(invocation -> {
@@ -1713,8 +2019,8 @@
     }
 
     private void setUpNullResponseForRequestTimeForNextSatelliteVisibility(
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -1726,8 +2032,8 @@
     }
 
     private void setUpResponseForRequestIsSatelliteProvisioned(
-            boolean isSatelliteProvisioned, @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            boolean isSatelliteProvisioned, @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         int[] provisioned = new int[] {isSatelliteProvisioned ? 1 : 0};
         doAnswer(invocation -> {
@@ -1739,8 +2045,8 @@
     }
 
     private void setUpResponseForRequestSatelliteEnabled(
-            boolean enabled, boolean demoMode, @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            boolean enabled, boolean demoMode, @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             if (exception == null && !enabled) {
@@ -1754,14 +2060,27 @@
                 .requestSatelliteEnabled(eq(enabled), eq(demoMode), any(Message.class));
     }
 
+    private void setUpResponseForRequestSetSatelliteEnabledForCarrier(
+            boolean enabled, @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
+                ? null : new SatelliteException(error);
+        doAnswer(invocation -> {
+            Message message = (Message) invocation.getArguments()[2];
+            AsyncResult.forMessage(message, null, exception);
+            message.sendToTarget();
+            return null;
+        }).when(mMockSatelliteModemInterface)
+                .requestSetSatelliteEnabledForCarrier(anyInt(), eq(enabled), any(Message.class));
+    }
+
     private void setUpNoResponseForRequestSatelliteEnabled(boolean enabled, boolean demoMode) {
         doNothing().when(mMockSatelliteModemInterface)
                 .requestSatelliteEnabled(eq(enabled), eq(demoMode), any(Message.class));
     }
 
     private void setUpResponseForProvisionSatelliteService(
-            String token, byte[] provisionData, @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            String token, byte[] provisionData, @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[2];
@@ -1778,8 +2097,8 @@
     }
 
     private void setUpResponseForDeprovisionSatelliteService(String token,
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[1];
@@ -1792,8 +2111,8 @@
 
     private void setUpResponseForRequestSatelliteCapabilities(
             SatelliteCapabilities satelliteCapabilities,
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -1820,8 +2139,8 @@
     }
 
     private void setUpNullResponseForRequestSatelliteCapabilities(
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -1832,8 +2151,8 @@
     }
 
     private void setUpResponseForStartSatelliteTransmissionUpdates(
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -1845,8 +2164,8 @@
     }
 
     private void setUpResponseForStopSatelliteTransmissionUpdates(
-            @SatelliteManager.SatelliteError int error) {
-        SatelliteException exception = (error == SATELLITE_ERROR_NONE)
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
@@ -2178,8 +2497,9 @@
         public boolean allRadiosDisabled = true;
         public int satelliteModeSettingValue = SATELLITE_MODE_ENABLED_FALSE;
 
-        TestSatelliteController(Context context, Looper looper) {
-            super(context, looper);
+        TestSatelliteController(
+                Context context, Looper looper, @NonNull FeatureFlags featureFlags) {
+            super(context, looper, featureFlags);
             logd("Constructing TestSatelliteController");
         }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
index 5e11297..3a108aa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.AsyncResult;
@@ -33,12 +34,12 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.telecom.Call;
 import android.telecom.Connection;
 import android.telephony.BinderCacheManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
 import android.telephony.satellite.SatelliteManager;
@@ -50,6 +51,7 @@
 import com.android.ims.ImsManager;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -85,6 +87,8 @@
     private Resources mResources;
     @Mock
     private ImsManager.MmTelFeatureConnectionFactory mMmTelFeatureConnectionFactory;
+    @Mock
+    private FeatureFlags mFeatureFlags;
     private TestConnection mTestConnection;
     private TestSOSMessageRecommender mTestSOSMessageRecommender;
 
@@ -105,8 +109,13 @@
                 .thenReturn("SubscriptionManager");
         when(mMockContext.getSystemService(SubscriptionManager.class))
                 .thenReturn(mSubscriptionManager);
+        when(mMockContext.getSystemServiceName(ActivityManager.class))
+                .thenReturn("ActivityManager");
+        when(mMockContext.getSystemService(ActivityManager.class))
+                .thenReturn(mActivityManager);
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         mTestSatelliteController = new TestSatelliteController(mMockContext,
-                Looper.myLooper());
+                Looper.myLooper(), mFeatureFlags);
         mTestImsManager = new TestImsManager(
                 mMockContext, PHONE_ID, mMmTelFeatureConnectionFactory, null, null, null);
         mTestConnection = new TestConnection(CALL_ID);
@@ -134,7 +143,7 @@
         processAllMessages();
 
         assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
-        assertTrue(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
     }
 
@@ -160,7 +169,7 @@
         mTestImsManager.sendImsRegistrationStateChangedEvent(true);
         processAllMessages();
 
-        assertFalse(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertUnregisterForStateChangedEventsTriggered(mPhone, 0, 0, 0);
@@ -173,7 +182,7 @@
         moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         processAllMessages();
 
-        assertTrue(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
     }
@@ -208,7 +217,7 @@
         moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         processAllMessages();
 
-        assertTrue(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertUnregisterForStateChangedEventsTriggered(mPhone, 2, 2, 2);
@@ -247,7 +256,7 @@
         moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         processAllMessages();
 
-        assertTrue(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
         /**
          * Since {@link SatelliteSOSMessageRecommender} always uses
          * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} when unregistering for provision
@@ -298,7 +307,7 @@
                 WRONG_CALL_ID, Connection.STATE_ACTIVE);
         processAllMessages();
 
-        assertFalse(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
@@ -325,7 +334,7 @@
     @Test
     public void testOnEmergencyCallStarted() {
         SatelliteController satelliteController = new SatelliteController(
-                mMockContext, Looper.myLooper());
+                mMockContext, Looper.myLooper(), mFeatureFlags);
         TestSOSMessageRecommender testSOSMessageRecommender = new TestSOSMessageRecommender(
                 Looper.myLooper(),
                 satelliteController, mTestImsManager,
@@ -349,7 +358,7 @@
         mTestSOSMessageRecommender.onEmergencyCallConnectionStateChanged(CALL_ID, connectionState);
         processAllMessages();
 
-        assertFalse(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
@@ -368,7 +377,7 @@
         mTestSOSMessageRecommender.sendServiceStateChangedEvent(availableServiceState);
         processAllMessages();
 
-        assertFalse(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertUnregisterForStateChangedEventsTriggered(mPhone, 0, 0, 0);
@@ -381,7 +390,7 @@
         moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         processAllMessages();
 
-        assertTrue(mTestConnection.isEventSent(Call.EVENT_DISPLAY_SOS_MESSAGE));
+        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
     }
@@ -421,8 +430,9 @@
          *
          * @param context The Context for the SatelliteController.
          */
-        protected TestSatelliteController(Context context, Looper looper) {
-            super(context, looper);
+        protected TestSatelliteController(
+                Context context, Looper looper, FeatureFlags featureFlags) {
+            super(context, looper, featureFlags);
             mProvisionStateChangedCallbacks = new HashMap<>();
         }
 
@@ -437,14 +447,14 @@
         }
 
         @Override
-        @SatelliteManager.SatelliteError public int registerForSatelliteProvisionStateChanged(
+        @SatelliteManager.SatelliteResult public int registerForSatelliteProvisionStateChanged(
                 int subId, @NonNull ISatelliteProvisionStateCallback callback) {
             mRegisterForSatelliteProvisionStateChangedCalls++;
             Set<ISatelliteProvisionStateCallback> perSubscriptionCallbacks =
                     mProvisionStateChangedCallbacks.getOrDefault(subId, new HashSet<>());
             perSubscriptionCallbacks.add(callback);
             mProvisionStateChangedCallbacks.put(subId, perSubscriptionCallbacks);
-            return SatelliteManager.SATELLITE_ERROR_NONE;
+            return SatelliteManager.SATELLITE_RESULT_SUCCESS;
         }
 
         @Override
@@ -464,7 +474,7 @@
             Bundle bundle = new Bundle();
             bundle.putBoolean(SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED,
                     mIsSatelliteCommunicationAllowed);
-            result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+            result.send(SatelliteManager.SATELLITE_RESULT_SUCCESS, bundle);
         }
 
         public void setIsSatelliteCommunicationAllowed(boolean allowed) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java
index ba1fb9e..78d7627 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java
@@ -157,6 +157,7 @@
         String plmn1 = "00101";
         String plmn2 = "00102";
         String plmn3 = "00103";
+        String plmn4 = "00104";
 
         Integer[] providerSupportedServicesForPlmn1 = {1, 2, 3};
         Integer[] providerSupportedServicesForPlmn2 = {3, 4};
@@ -165,8 +166,9 @@
                 plmn1, new HashSet<>(Arrays.asList(providerSupportedServicesForPlmn1)));
         providerSupportedServicesMap.put(
                 plmn2, new HashSet<>(Arrays.asList(providerSupportedServicesForPlmn2)));
+        providerSupportedServicesMap.put(plmn4, new HashSet<>());
 
-        Integer[] carrierSupportedServicesForPlmn2 = {3};
+        Integer[] carrierSupportedServicesForPlmn2 = {};
         Integer[] carrierSupportedServicesForPlmn3 = {1, 3, 4};
         Map<String, Set<Integer>> carrierSupportedServicesMap = new HashMap<>();
         carrierSupportedServicesMap.put(
@@ -174,24 +176,25 @@
         carrierSupportedServicesMap.put(
                 plmn3, new HashSet<>(Arrays.asList(carrierSupportedServicesForPlmn3)));
 
-        // {@code plmn1} is present in only provider support services.
+        // Come from device config.
         int[] expectedSupportedServicesForPlmn1 = {1, 2, 3};
-        // Intersection of {3,4} and {3}.
-        int[] expectedSupportedServicesForPlmn2 = {3};
+        // Come from carrier config.
+        int[] expectedSupportedServicesForPlmn3 = {1, 3, 4};
         Map<String, Set<Integer>> supportedServicesMap =
                 SatelliteServiceUtils.mergeSupportedSatelliteServices(
                         providerSupportedServicesMap, carrierSupportedServicesMap);
 
         assertEquals(2, supportedServicesMap.size());
         assertTrue(supportedServicesMap.containsKey(plmn1));
-        assertTrue(supportedServicesMap.containsKey(plmn2));
-        assertFalse(supportedServicesMap.containsKey(plmn3));
+        assertTrue(supportedServicesMap.containsKey(plmn3));
+        assertFalse(supportedServicesMap.containsKey(plmn2));
+        assertFalse(supportedServicesMap.containsKey(plmn4));
         assertTrue(Arrays.equals(expectedSupportedServicesForPlmn1,
                 supportedServicesMap.get(plmn1).stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
-        assertTrue(Arrays.equals(expectedSupportedServicesForPlmn2,
-                supportedServicesMap.get(plmn2).stream()
+        assertTrue(Arrays.equals(expectedSupportedServicesForPlmn3,
+                supportedServicesMap.get(plmn3).stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
index 3ccf512..888b014 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -26,10 +26,13 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.Looper;
 import android.os.Message;
 import android.telephony.satellite.ISatelliteStateCallback;
@@ -57,33 +60,48 @@
 @TestableLooper.RunWithLooper
 public class SatelliteSessionControllerTest extends TelephonyTest {
     private static final String TAG = "SatelliteSessionControllerTest";
-    private static final long TEST_SATELLITE_STAY_AT_LISTENING_MILLIS = 200;
-    private static final long EVENT_PROCESSING_TIME_MILLIS = 100;
+    private static final int TEST_SATELLITE_TIMEOUT_MILLIS = 200;
+    private static final int EVENT_PROCESSING_TIME_MILLIS = 100;
 
     private static final String STATE_UNAVAILABLE = "UnavailableState";
     private static final String STATE_POWER_OFF = "PowerOffState";
     private static final String STATE_IDLE = "IdleState";
     private static final String STATE_TRANSFERRING = "TransferringState";
     private static final String STATE_LISTENING = "ListeningState";
+    private static final String STATE_NOT_CONNECTED = "NotConnectedState";
+    private static final String STATE_CONNECTED = "ConnectedState";
 
     private TestSatelliteModemInterface mSatelliteModemInterface;
     private TestSatelliteSessionController mTestSatelliteSessionController;
     private TestSatelliteStateCallback mTestSatelliteStateCallback;
 
-    @Mock
-    private SatelliteController mSatelliteController;
+    @Mock private SatelliteController mMockSatelliteController;
+    @Mock private DatagramReceiver mMockDatagramReceiver;
+    @Mock private DatagramDispatcher mMockDatagramDispatcher;
+    @Mock private DatagramController mMockDatagramController;
 
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         MockitoAnnotations.initMocks(this);
 
+        replaceInstance(DatagramReceiver.class, "sInstance", null,
+                mMockDatagramReceiver);
+        replaceInstance(DatagramDispatcher.class, "sInstance", null,
+                mMockDatagramDispatcher);
+        replaceInstance(SatelliteController.class, "sInstance", null,
+                mMockSatelliteController);
+        replaceInstance(DatagramController.class, "sInstance", null,
+                mMockDatagramController);
+
+        Resources resources = mContext.getResources();
+        when(resources.getInteger(anyInt())).thenReturn(TEST_SATELLITE_TIMEOUT_MILLIS);
+
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(false);
         mSatelliteModemInterface = new TestSatelliteModemInterface(
-                mContext, mSatelliteController, Looper.myLooper());
+                mContext, mMockSatelliteController, Looper.myLooper());
         mTestSatelliteSessionController = new TestSatelliteSessionController(mContext,
-                Looper.myLooper(), true, mSatelliteModemInterface,
-                TEST_SATELLITE_STAY_AT_LISTENING_MILLIS,
-                TEST_SATELLITE_STAY_AT_LISTENING_MILLIS);
+                Looper.myLooper(), true, mSatelliteModemInterface);
         processAllMessages();
 
         mTestSatelliteStateCallback = new TestSatelliteStateCallback();
@@ -105,8 +123,7 @@
          * state.
          */
         TestSatelliteSessionController sessionController1 = new TestSatelliteSessionController(
-                mContext, Looper.myLooper(), false,
-                mSatelliteModemInterface, 100, 100);
+                mContext, Looper.myLooper(), false, mSatelliteModemInterface);
         assertNotNull(sessionController1);
         processAllMessages();
         assertEquals(STATE_UNAVAILABLE, sessionController1.getCurrentStateName());
@@ -115,8 +132,7 @@
          * Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
          */
         TestSatelliteSessionController sessionController2 = new TestSatelliteSessionController(
-                mContext, Looper.myLooper(), true,
-                mSatelliteModemInterface, 100, 100);
+                mContext, Looper.myLooper(), true, mSatelliteModemInterface);
         assertNotNull(sessionController2);
         processAllMessages();
         assertEquals(STATE_POWER_OFF, sessionController2.getCurrentStateName());
@@ -129,8 +145,7 @@
          * state.
          */
         TestSatelliteSessionController sessionController = new TestSatelliteSessionController(
-                mContext, Looper.myLooper(), false,
-                mSatelliteModemInterface, 100, 100);
+                mContext, Looper.myLooper(), false, mSatelliteModemInterface);
         assertNotNull(sessionController);
         processAllMessages();
         assertEquals(STATE_UNAVAILABLE, sessionController.getCurrentStateName());
@@ -305,7 +320,7 @@
         assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
 
         // Wait for timeout
-        moveTimeForward(TEST_SATELLITE_STAY_AT_LISTENING_MILLIS);
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
         processAllMessages();
 
         // SatelliteSessionController should move to IDLE state after timeout
@@ -384,9 +399,328 @@
         assertFalse(mTestSatelliteSessionController.isSendingTriggeredDuringTransferringState());
     }
 
+    @Test
+    public void testStateTransitionForNbIot() {
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+
+        /**
+         * Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+         */
+        assertNotNull(mTestSatelliteSessionController);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        setupDatagramTransferringState(false);
+
+        // Power on the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(true);
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem
+        // is powered on.
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
+        processAllMessages();
+        // SatelliteSessionController should stay at NOT_CONNECTED state.
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteStateCallback);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        setupDatagramTransferringState(true);
+
+        // Power off the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false);
+        processAllMessages();
+
+        // SatelliteSessionController should move back to POWER_OFF state.
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Power on the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(true);
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after radio is turned on.
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        // Start sending datagrams
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+        processAllMessages();
+
+        // The datagram sending event should be ignored.
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteStateCallback);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Satellite modem is connected to a satellite network.
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+
+        // SatelliteSessionController should move to CONNECTED state
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        // Start sending datagrams
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+        processAllMessages();
+
+        // SatelliteSessionController should move to TRANSFERRING state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+        assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        // Sending datagrams failed
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+        processAllMessages();
+
+        // SatelliteSessionController should move to CONNECTED state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        // Start sending datagrams again
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+        processAllMessages();
+
+        // SatelliteSessionController should move to TRANSFERRING state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+        assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        // Sending datagrams is successful and done.
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+        processAllMessages();
+
+        // SatelliteSessionController should move to CONNECTED state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        // Start receiving datagrams
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING);
+        processAllMessages();
+
+        // SatelliteSessionController should move to TRANSFERRING state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+        assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        // Receiving datagrams is successful and done.
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+        processAllMessages();
+
+        // SatelliteSessionController should move to CONNECTED state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        // Wait for timeout
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
+        processAllMessages();
+
+        // SatelliteSessionController should move to IDLE state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        // Start sending datagrams
+        mTestSatelliteSessionController.onSatelliteDatagramsTransferRequested();
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Satellite modem is connected to a satellite network.
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+
+        // SatelliteSessionController should move to CONNECTED state
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Satellite modem is disconnected from the satellite network.
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Satellite modem is connected to a satellite network.
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+
+        // SatelliteSessionController should move to CONNECTED state
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Power off the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false);
+        processAllMessages();
+
+        // SatelliteSessionController should move to POWER_OFF state.
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Power on the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(true);
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem
+        // is powered on.
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Satellite modem is connected to a satellite network.
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+
+        // SatelliteSessionController should move to CONNECTED state
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Wait for timeout
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
+        processAllMessages();
+
+        // SatelliteSessionController should move to IDLE state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Set up error response for the request to disable cellular scanning
+        mSatelliteModemInterface.setErrorCode(SatelliteManager.SATELLITE_RESULT_MODEM_ERROR);
+
+        // Start sending datagrams
+        mTestSatelliteSessionController.onSatelliteDatagramsTransferRequested();
+        processAllMessages();
+
+        // SatelliteSessionController should stay at IDLE state because it failed to disable
+        // cellular scanning.
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteStateCallback);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+
+        mSatelliteModemInterface.setErrorCode(SatelliteManager.SATELLITE_RESULT_SUCCESS);
+
+        // Power off the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false);
+        processAllMessages();
+
+        // SatelliteSessionController should move to POWER_OFF
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Power on the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(true);
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem
+        // is powered on.
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
+        processAllMessages();
+
+        // SatelliteSessionController should move to IDLE state because NB-IOT inactivity timer has
+        // timed out.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Power off the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false);
+        processAllMessages();
+
+        // SatelliteSessionController should move to POWER_OFF
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Power on the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(true);
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem
+        // is powered on.
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Start sending datagrams and the NB-IOT inactivity timer should be stopped.
+        mTestSatelliteSessionController.onSatelliteDatagramsTransferRequested();
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
+        processAllMessages();
+
+        // SatelliteSessionController should stay at NOT_CONNECTED state because.
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteStateCallback);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Transferring datagram failed because satellite failed to connect to a satellite network.
+        // The NB-IOT inactivity timer should be started.
+        mTestSatelliteSessionController.onDatagramWaitForConnectedStateTimerTimedOut();
+        processAllMessages();
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
+        processAllMessages();
+
+        // SatelliteSessionController should move to IDLE state because NB-IOT inactivity timer has
+        // timed out.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+    }
+
+    private void setupDatagramTransferringState(boolean isTransferring) {
+        when(mMockDatagramController.isSendingInIdleState()).thenReturn(isTransferring);
+        when(mMockDatagramController.isPollingInIdleState()).thenReturn(isTransferring);
+    }
+
     private static class TestSatelliteModemInterface extends SatelliteModemInterface {
         private final AtomicInteger mListeningEnabledCount = new AtomicInteger(0);
         private final AtomicInteger mListeningDisabledCount = new AtomicInteger(0);
+        @SatelliteManager.SatelliteResult
+        private int mErrorCode = SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
         TestSatelliteModemInterface(@NonNull Context context,
                 SatelliteController satelliteController, @NonNull Looper looper) {
@@ -411,6 +745,14 @@
             else mListeningDisabledCount.incrementAndGet();
         }
 
+        @Override
+        public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+                @Nullable Message message) {
+            if (message != null) {
+                sendMessageWithResult(message, null, mErrorCode);
+            }
+        }
+
         public int getListeningEnabledCount() {
             return mListeningEnabledCount.get();
         }
@@ -418,25 +760,29 @@
         public int getListeningDisabledCount() {
             return mListeningDisabledCount.get();
         }
+
+        public void setErrorCode(@SatelliteManager.SatelliteResult int errorCode) {
+            mErrorCode = errorCode;
+        }
     }
 
     private static class TestSatelliteSessionController extends SatelliteSessionController {
         TestSatelliteSessionController(Context context, Looper looper, boolean isSatelliteSupported,
-                SatelliteModemInterface satelliteModemInterface,
-                long satelliteStayAtListeningFromSendingMillis,
-                long satelliteStayAtListeningFromReceivingMillis) {
-            super(context, looper, isSatelliteSupported, satelliteModemInterface,
-                    satelliteStayAtListeningFromSendingMillis,
-                    satelliteStayAtListeningFromReceivingMillis);
+                SatelliteModemInterface satelliteModemInterface) {
+            super(context, looper, isSatelliteSupported, satelliteModemInterface);
         }
 
-        public String getCurrentStateName() {
+        String getCurrentStateName() {
             return getCurrentState().getName();
         }
 
-        public boolean isSendingTriggeredDuringTransferringState() {
+        boolean isSendingTriggeredDuringTransferringState() {
             return mIsSendingTriggeredDuringTransferringState.get();
         }
+
+        boolean isNbIotInactivityTimerStarted() {
+            return hasMessages(EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT);
+        }
     }
 
     private static class TestSatelliteStateCallback extends ISatelliteStateCallback.Stub {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
index 0358809..59dfec6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
@@ -117,6 +117,8 @@
     static final int FAKE_TP_MESSAGE_REFERENCE2 = 456;
     static final int FAKE_USER_ID1 = 10;
     static final int FAKE_USER_ID2 = 11;
+    static final int FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED = 1;
+    static final int FAKE_SATELLITE_ATTACH_FOR_CARRIER_DISABLED = 0;
 
     static final String FAKE_MAC_ADDRESS1 = "DC:E5:5B:38:7D:40";
     static final String FAKE_MAC_ADDRESS2 = "DC:B5:4F:47:F3:4C";
@@ -186,6 +188,8 @@
                     .setLastUsedTPMessageReference(FAKE_TP_MESSAGE_REFERENCE1)
                     .setUserId(FAKE_USER_ID1)
                     .setSatelliteEnabled(0)
+                    .setSatelliteAttachEnabledForCarrier(
+                            FAKE_SATELLITE_ATTACH_FOR_CARRIER_DISABLED)
                     .setGroupDisabled(false)
                     .build();
 
@@ -254,6 +258,8 @@
                     .setLastUsedTPMessageReference(FAKE_TP_MESSAGE_REFERENCE2)
                     .setUserId(FAKE_USER_ID2)
                     .setSatelliteEnabled(1)
+                    .setSatelliteAttachEnabledForCarrier(
+                            FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED)
                     .setGroupDisabled(false)
                     .build();
 
@@ -1930,6 +1936,41 @@
     }
 
     @Test
+    public void testUpdateCarrierHandoverToSatelliteEnabled() throws Exception {
+        // exception is expected if there is nothing in the database.
+        assertThrows(IllegalArgumentException.class,
+                () -> mDatabaseManagerUT.setSatelliteAttachEnabledForCarrier(
+                        FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                        FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED));
+
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setSatelliteAttachEnabledForCarrier(
+                FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setSatelliteAttachEnabledForCarrier(
+                        FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED)
+                .build();
+        verifySubscription(subInfo);
+        verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+        assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+                FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER))
+                .isEqualTo(FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED);
+
+        mDatabaseManagerUT.setSubscriptionProperty(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
+                FAKE_SATELLITE_ATTACH_FOR_CARRIER_DISABLED);
+        assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(
+                FAKE_SUBSCRIPTION_INFO1.getSubscriptionId())
+                .getSatelliteAttachEnabledForCarrier())
+                .isEqualTo(FAKE_SATELLITE_ATTACH_FOR_CARRIER_DISABLED);
+    }
+
+    @Test
     public void testUpdateSubscriptionsInGroup() throws Exception {
         insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
         insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO2);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
index e03256b..d1e3bb5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
@@ -101,6 +101,9 @@
                             .FAKE_TP_MESSAGE_REFERENCE1)
                     .setUserId(SubscriptionDatabaseManagerTest.FAKE_USER_ID1)
                     .setSatelliteEnabled(1)
+                    .setSatelliteAttachEnabledForCarrier(
+                            SubscriptionDatabaseManagerTest
+                                    .FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED)
                     .setGroupDisabled(false)
                     .build();
 
@@ -211,6 +214,9 @@
                 SubscriptionDatabaseManagerTest.FAKE_TP_MESSAGE_REFERENCE1);
         assertThat(mSubInfo.getUserId()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_USER_ID1);
         assertThat(mSubInfo.getSatelliteEnabled()).isEqualTo(1);
+        assertThat(mSubInfo.getSatelliteAttachEnabledForCarrier())
+                .isEqualTo(SubscriptionDatabaseManagerTest
+                        .FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED);
         assertThat(mSubInfo.isGroupDisabled()).isFalse();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
index aea7965..ab531a6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
@@ -104,6 +104,7 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.euicc.EuiccController;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.SubscriptionProvider;
 import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback;
 import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionMap;
@@ -153,7 +154,7 @@
     // mocked
     private SubscriptionManagerServiceCallback mMockedSubscriptionManagerServiceCallback;
     private EuiccController mEuiccController;
-
+    private FeatureFlags mFlags;
     private Set<Integer> mActiveSubs = new ArraySet<>();
 
     @Rule
@@ -199,7 +200,9 @@
         ((MockContentResolver) mContext.getContentResolver()).addProvider(
                 Telephony.Carriers.CONTENT_URI.getAuthority(), mSubscriptionProvider);
 
-        mSubscriptionManagerServiceUT = new SubscriptionManagerService(mContext, Looper.myLooper());
+        mFlags = Mockito.mock(FeatureFlags.class);
+        mSubscriptionManagerServiceUT = new SubscriptionManagerService(mContext, Looper.myLooper(),
+                mFlags);
 
         monitorTestableLooper(new TestableLooper(getBackgroundHandler().getLooper()));
         monitorTestableLooper(new TestableLooper(getSubscriptionDatabaseManager().getLooper()));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
index 2ab23f3..9265a62 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -495,40 +495,6 @@
         assertEquals(uiccCardInfo, mUiccControllerUT.getAllUiccCardInfos().get(0));
     }
 
-    @Test
-    public void testEidNotSupported() {
-        // Give UiccController a real context so it can use shared preferences
-        mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
-
-        // Mock out UiccSlots
-        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
-        doReturn(true).when(mMockSlot).isEuicc();
-        doReturn(mMockEuiccCard).when(mMockSlot).getUiccCard();
-        doReturn(null).when(mMockEuiccCard).getEid();
-
-        // simulate card status loaded so that the UiccController sets the card ID
-        IccCardStatus ics = new IccCardStatus();
-        ics.setCardState(1 /* present */);
-        ics.setUniversalPinState(3 /* disabled */);
-        ics.atr = "abcdef0123456789abcdef";
-        ics.iccid = "123451234567890";
-        ics.mSlotPortMapping = new IccSlotPortMapping();
-        ics.mSlotPortMapping.mPhysicalSlotIndex = UiccController.INVALID_SLOT_ID;
-        // make it seem like EID is not supported by setting physical slot = -1 like on HAL < 1.2
-
-        mSimulatedCommands.setSupportsEid(false);
-
-        AsyncResult ar = new AsyncResult(null, ics, null);
-        Message msg = Message.obtain(mUiccControllerUT, EVENT_GET_ICC_STATUS_DONE, ar);
-        mUiccControllerUT.handleMessage(msg);
-
-        // assert that the default eUICC card Id is UNSUPPORTED_CARD_ID
-        assertEquals(TelephonyManager.UNSUPPORTED_CARD_ID,
-                mUiccControllerUT.getCardIdForDefaultEuicc());
-
-        mSimulatedCommands.setSupportsEid(true);
-    }
-
     /**
      * The default eUICC should not be the removable slot if there is a built-in eUICC.
      */