[automerger skipped] Merge "Added new testNotifyCellLocationForSubscriberByUserSwitched" am: 93cc1d5336 am: 062a26cd3e am: 340e9f6ddd -s ours

am skip reason: skip tag Change-Id I111eb3569b6f30d0ca5bb50a9e04770a1f04fdd7 with SHA-1 6121a24c3e is already in history

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

Change-Id: I6409fa728b699e385e34e428ea1b1d6070ff525b
diff --git a/jarjar-rules-shared.txt b/jarjar-rules-shared.txt
index 5635b18..4f3f617 100644
--- a/jarjar-rules-shared.txt
+++ b/jarjar-rules-shared.txt
@@ -4,6 +4,7 @@
 rule android.os.Registrant* com.android.internal.telephony.Registrant@1
 rule android.hidl.** android.internal.hidl.@1
 rule android.sysprop.** android.internal.telephony.sysprop.@1
+rule android.util.IndentingPrintWriter* com.android.internal.telephony.AndroidUtilIndentingPrintWriter@1
 rule android.util.LocalLog* com.android.internal.telephony.LocalLog@1
 rule android.util.TimeUtils* com.android.internal.telephony.TimeUtils@1
 rule com.android.internal.os.SomeArgs* com.android.internal.telephony.SomeArgs@1
diff --git a/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java b/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java
index 7271bf3..40e9a1c 100644
--- a/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java
@@ -18,7 +18,9 @@
 package com.android.internal.telephony;
 
 import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMapClient;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.net.Uri;
@@ -40,10 +42,6 @@
      */
     public void sendText(Context context, String destAddr, String text, PendingIntent sentIntent,
             PendingIntent deliveryIntent, SubscriptionInfo info) {
-        /*
-        This is to remove the usage of hidden constant MAP_CLIENT and hidden API
-        BluetoothMapClient.sendMessage(). This code is currently not functional anyway; it will be
-        re-enabled in a later release.
         BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
         if (btAdapter == null) {
             // No bluetooth service on this platform?
@@ -56,10 +54,11 @@
             sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS);
             return;
         }
-        btAdapter.getProfileProxy(context.getApplicationContext(),
+        if (btAdapter.getProfileProxy(context.getApplicationContext(),
                 new MapMessageSender(destAddr, text, device, sentIntent, deliveryIntent),
-                BluetoothProfile.MAP_CLIENT);
-        */
+                BluetoothProfile.MAP_CLIENT)) {
+            return;
+        }
         throw new RuntimeException("Can't send message through BluetoothMapClient");
     }
 
@@ -100,7 +99,6 @@
         @Override
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
             Log.d(LOG_TAG, "Service connected");
-            /*
             if (profile != BluetoothProfile.MAP_CLIENT) {
                 return;
             }
@@ -112,8 +110,6 @@
             }
             BluetoothAdapter.getDefaultAdapter()
                     .closeProfileProxy(BluetoothProfile.MAP_CLIENT, mapProfile);
-            */
-            throw new RuntimeException("Can't send message through BluetoothMapClient");
         }
 
         @Override
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index c73214a..00f8c39 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -254,7 +254,13 @@
             return;
         }
         mLastAccessResetCarrierKey = now;
-        deleteCarrierInfoForImsiEncryption(context);
+        int[] subIds = context.getSystemService(SubscriptionManager.class)
+                .getSubscriptionIds(mPhoneId);
+        if (subIds == null || subIds.length < 1) {
+            Log.e(LOG_TAG, "Could not reset carrier keys, subscription for mPhoneId=" + mPhoneId);
+            return;
+        }
+        deleteCarrierInfoForImsiEncryption(context, subIds[0]);
         Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
         SubscriptionManager.putPhoneIdAndSubIdExtra(resetIntent, mPhoneId);
         context.sendBroadcastAsUser(resetIntent, UserHandle.ALL);
@@ -264,12 +270,12 @@
      * Deletes all the keys for a given Carrier from the device keystore.
      * @param context Context
      */
-    public static void deleteCarrierInfoForImsiEncryption(Context context) {
-        Log.i(LOG_TAG, "deleting carrier key from db");
+    public static void deleteCarrierInfoForImsiEncryption(Context context, int subId) {
+        Log.i(LOG_TAG, "deleting carrier key from db for subId=" + subId);
         String mcc = "";
         String mnc = "";
-        final TelephonyManager telephonyManager =
-                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
+                .createForSubscriptionId(subId);
         String simOperator = telephonyManager.getSimOperator();
         if (!TextUtils.isEmpty(simOperator)) {
             mcc = simOperator.substring(0, 3);
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index c7e34a0..64dc7ec 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -32,9 +32,9 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
-import android.telephony.TelephonyCallback;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyManager.NetworkTypeBitMask;
 
diff --git a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
index 8d6985d..8379824 100644
--- a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
+++ b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
@@ -43,7 +43,13 @@
 
 /**
  * Filters incoming SMS with carrier services.
- * <p> A new instance must be created for filtering each message.
+ *
+ * <p>A new instance must be created for filtering each message.
+ *
+ * <p>Note that if a carrier services app is unavailable at the time a message is received because
+ * credential-encrypted storage is unavailable and it is not direct-boot aware, and the message ends
+ * up being handled by a filter further down the chain, that message will not be redelivered to the
+ * carrier app once the user unlocks the storage.
  */
 public class CarrierServicesSmsFilter {
     protected static final boolean DBG = true;
@@ -238,9 +244,10 @@
         void filterSms(CarrierSmsFilterCallback smsFilterCallback) {
             mSmsFilterCallback = smsFilterCallback;
             if (!mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
-                    mContext, mPackageName, ()-> onServiceReady())) {
+                    mContext, mPackageName, runnable -> runnable.run(), ()-> onServiceReady())) {
                 loge("CarrierSmsFilter::filterSms: bindService() for failed for " + mPackageName);
-                smsFilterCallback.onFilterComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
+                smsFilterCallback.onReceiveSmsComplete(
+                        CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
             } else {
                 logv("CarrierSmsFilter::filterSms: bindService() for succeeded for "
                         + mPackageName);
@@ -254,12 +261,12 @@
         private void onServiceReady() {
             try {
                 log("onServiceReady: calling filterSms on " + mPackageName);
-                mCarrierMessagingServiceWrapper.filterSms(
+                mCarrierMessagingServiceWrapper.receiveSms(
                         new MessagePdu(Arrays.asList(mPdus)), mSmsFormat, mDestPort,
-                        mPhone.getSubId(), mSmsFilterCallback);
+                        mPhone.getSubId(), runnable -> runnable.run(), mSmsFilterCallback);
             } catch (RuntimeException e) {
                 loge("Exception filtering the SMS with " + mPackageName + ": " + e);
-                mSmsFilterCallback.onFilterComplete(
+                mSmsFilterCallback.onReceiveSmsComplete(
                         CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
             }
         }
@@ -287,14 +294,14 @@
          * This method should be called only once.
          */
         @Override
-        public void onFilterComplete(int result) {
+        public void onReceiveSmsComplete(int result) {
             log("CarrierSmsFilterCallback::onFilterComplete: Called from " + mPackageName
                     + " with result: " + result);
             // in the case that timeout has already passed and triggered, but the initial callback
             // is run afterwards, we should not follow through
             if (!mIsOnFilterCompleteCalled) {
                 mIsOnFilterCompleteCalled = true;
-                mCarrierMessagingServiceWrapper.disposeConnection(mContext);
+                mCarrierMessagingServiceWrapper.disconnect();
                 mFilterAggregator.onFilterComplete(result);
             }
         }
@@ -394,7 +401,7 @@
         private void handleFilterCallbacksTimeout() {
             for (CarrierSmsFilterCallback callback : mFilterAggregator.mCallbacks) {
                 log("handleFilterCallbacksTimeout: calling onFilterComplete");
-                callback.onFilterComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
+                callback.onReceiveSmsComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
             }
         }
     }
diff --git a/src/java/com/android/internal/telephony/CellularNetworkValidator.java b/src/java/com/android/internal/telephony/CellularNetworkValidator.java
index cee9cea..7124703 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkValidator.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkValidator.java
@@ -235,7 +235,7 @@
      */
     public boolean isValidationFeatureSupported() {
         return PhoneConfigurationManager.getInstance().getCurrentPhoneCapability()
-                .validationBeforeSwitchSupported;
+                .isNetworkValidationBeforeSwitchSupported();
     }
 
     @VisibleForTesting
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 7572d11..6ae0b1f 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -34,7 +34,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
-import android.telephony.data.SliceInfo;
+import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.TrafficDescriptor;
 import android.telephony.emergency.EmergencyNumber;
 
@@ -1874,8 +1874,8 @@
      */
     void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
             boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
-            Message result);
+            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+            boolean matchAllRuleAllowed, Message result);
 
     /**
      * Deactivate packet data connection
@@ -2637,7 +2637,6 @@
      */
     default void cancelHandover(Message result, int callId) {};
 
-
     /**
      * Control the data throttling at modem.
      *
@@ -2650,4 +2649,12 @@
      */
     default void setDataThrottling(Message result, WorkSource workSource,
             int dataThrottlingAction, long completionWindowMillis) {};
+
+    /**
+     * Request to get the current slicing configuration including URSP rules and
+     * NSSAIs (configured, allowed and rejected).
+     *
+     * @param result Message that will be sent back to handler.
+     */
+    default void getSlicingConfig(Message result) {};
 }
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index 0997186..1752d90 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
 import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE;
 import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED;
 import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE;
@@ -25,7 +26,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Configuration;
 import android.hardware.display.DisplayManager;
 import android.hardware.radio.V1_5.IndicationFilter;
 import android.net.ConnectivityManager;
@@ -71,7 +71,7 @@
     protected static final String TAG = DeviceStateMonitor.class.getSimpleName();
 
     static final int EVENT_RIL_CONNECTED                = 0;
-    static final int EVENT_CAR_MODE_CHANGED             = 1;
+    static final int EVENT_AUTOMOTIVE_PROJECTION_STATE_CHANGED = 1;
     @VisibleForTesting
     static final int EVENT_SCREEN_STATE_CHANGED         = 2;
     static final int EVENT_POWER_SAVE_MODE_CHANGED      = 3;
@@ -173,11 +173,11 @@
     private boolean mIsWifiConnected;
 
     /**
-     * Car mode is on. True means the device is currently connected to Android Auto. This should be
-     * handled by mIsScreenOn, but the Android Auto display is private and not accessible by
-     * DeviceStateMonitor from DisplayMonitor.
+     * Automotive projection is active. True means the device is currently connected to Android
+     * Auto. This should be handled by mIsScreenOn, but the Android Auto display is private and not
+     * accessible by DeviceStateMonitor from DisplayMonitor.
      */
-    private boolean mIsCarModeOn;
+    private boolean mIsAutomotiveProjectionActive;
 
     /**
      * True indicates we should always enable the signal strength reporting from radio.
@@ -248,14 +248,6 @@
                     msg = obtainMessage(EVENT_TETHERING_STATE_CHANGED);
                     msg.arg1 = isTetheringOn ? 1 : 0;
                     break;
-                case UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED:
-                    msg = obtainMessage(EVENT_CAR_MODE_CHANGED);
-                    msg.arg1 = 1; // car mode on
-                    break;
-                case UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED:
-                    msg = obtainMessage(EVENT_CAR_MODE_CHANGED);
-                    msg.arg1 = 0; // car mode off
-                    break;
                 default:
                     log("Unexpected broadcast intent: " + intent, false);
                     return;
@@ -279,7 +271,7 @@
         mIsPowerSaveOn = isPowerSaveModeOn();
         mIsCharging = isDeviceCharging();
         mIsScreenOn = isScreenOn();
-        mIsCarModeOn = isCarModeOn();
+        mIsAutomotiveProjectionActive = isAutomotiveProjectionActive();
         // Assuming tethering is always off after boot up.
         mIsTetheringOn = false;
         mIsLowDataExpected = false;
@@ -289,7 +281,7 @@
                 + ", mIsCharging=" + mIsCharging
                 + ", mIsPowerSaveOn=" + mIsPowerSaveOn
                 + ", mIsLowDataExpected=" + mIsLowDataExpected
-                + ", mIsCarModeOn=" + mIsCarModeOn
+                + ", mIsAutomotiveProjectionActive=" + mIsAutomotiveProjectionActive
                 + ", mIsWifiConnected=" + mIsWifiConnected
                 + ", mIsAlwaysSignalStrengthReportingEnabled="
                 + mIsAlwaysSignalStrengthReportingEnabled, false);
@@ -299,8 +291,6 @@
         filter.addAction(BatteryManager.ACTION_CHARGING);
         filter.addAction(BatteryManager.ACTION_DISCHARGING);
         filter.addAction(TetheringManager.ACTION_TETHER_STATE_CHANGED);
-        filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
-        filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
         mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone);
 
         mPhone.mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null);
@@ -309,6 +299,16 @@
         ConnectivityManager cm = (ConnectivityManager) phone.getContext().getSystemService(
                 Context.CONNECTIVITY_SERVICE);
         cm.registerNetworkCallback(mWifiNetworkRequest, mNetworkCallback);
+
+        UiModeManager umm = (UiModeManager) phone.getContext().getSystemService(
+                Context.UI_MODE_SERVICE);
+        umm.addOnProjectionStateChangeListener(PROJECTION_TYPE_AUTOMOTIVE,
+                phone.getContext().getMainExecutor(),
+                (t, pkgs) -> {
+                    Message msg = obtainMessage(EVENT_AUTOMOTIVE_PROJECTION_STATE_CHANGED);
+                    msg.arg1 = Math.min(pkgs.size(), 1);
+                    sendMessage(msg);
+                });
     }
 
     /**
@@ -403,8 +403,8 @@
         // 1. The device is charging.
         // 2. When the screen is on.
         // 3. When the tethering is on.
-        // 4. When car mode (Android Auto) is on.
-        return mIsCharging || mIsScreenOn || mIsTetheringOn || mIsCarModeOn;
+        // 4. When automotive projection (Android Auto) is on.
+        return mIsCharging || mIsScreenOn || mIsTetheringOn || mIsAutomotiveProjectionActive;
     }
 
     /**
@@ -460,7 +460,7 @@
             case EVENT_CHARGING_STATE_CHANGED:
             case EVENT_TETHERING_STATE_CHANGED:
             case EVENT_UPDATE_ALWAYS_REPORT_SIGNAL_STRENGTH:
-            case EVENT_CAR_MODE_CHANGED:
+            case EVENT_AUTOMOTIVE_PROJECTION_STATE_CHANGED:
                 onUpdateDeviceState(msg.what, msg.arg1 != 0);
                 break;
             case EVENT_WIFI_CONNECTION_CHANGED:
@@ -507,9 +507,9 @@
                 if (mIsAlwaysSignalStrengthReportingEnabled == state) return;
                 mIsAlwaysSignalStrengthReportingEnabled = state;
                 break;
-            case EVENT_CAR_MODE_CHANGED:
-                if (mIsCarModeOn == state) return;
-                mIsCarModeOn = state;
+            case EVENT_AUTOMOTIVE_PROJECTION_STATE_CHANGED:
+                if (mIsAutomotiveProjectionActive == state) return;
+                mIsAutomotiveProjectionActive = state;
                 break;
             default:
                 return;
@@ -740,15 +740,16 @@
     }
 
     /**
-     * @return True if car mode (Android Auto) is on.
+     * @return True if automotive projection (Android Auto) is active.
      */
-    private boolean isCarModeOn() {
+    private boolean isAutomotiveProjectionActive() {
         final UiModeManager umm = (UiModeManager) mPhone.getContext().getSystemService(
                 Context.UI_MODE_SERVICE);
         if (umm == null) return false;
-        boolean retval = umm.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR;
-        log("isCarModeOn=" + retval, true);
-        return retval;
+        boolean isAutomotiveProjectionActive = (umm.getActiveProjectionTypes()
+                & PROJECTION_TYPE_AUTOMOTIVE) != 0;
+        log("isAutomotiveProjectionActive=" + isAutomotiveProjectionActive, true);
+        return isAutomotiveProjectionActive;
     }
 
     /**
@@ -799,7 +800,7 @@
         ipw.println("mIsCharging=" + mIsCharging);
         ipw.println("mIsPowerSaveOn=" + mIsPowerSaveOn);
         ipw.println("mIsLowDataExpected=" + mIsLowDataExpected);
-        ipw.println("mIsCarModeOn=" + mIsCarModeOn);
+        ipw.println("mIsAutomotiveProjectionActive=" + mIsAutomotiveProjectionActive);
         ipw.println("mUnsolicitedResponseFilter=" + mUnsolicitedResponseFilter);
         ipw.println("mIsWifiConnected=" + mIsWifiConnected);
         ipw.println("mIsAlwaysSignalStrengthReportingEnabled="
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index cdf1f42..e9ecb79 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -559,6 +559,9 @@
             case CallFailCause.USER_ALERTING_NO_ANSWER:
                 return DisconnectCause.TIMED_OUT;
 
+            case CallFailCause.RADIO_OFF:
+                return DisconnectCause.POWER_OFF;
+
             case CallFailCause.ACCESS_CLASS_BLOCKED:
             case CallFailCause.ERROR_UNSPECIFIED:
             case CallFailCause.NORMAL_CLEARING:
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index bd2f3a8..494ca06 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -89,6 +89,7 @@
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.dataconnection.DataEnabledSettings;
 import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
 import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.gsm.GsmMmiCode;
@@ -198,7 +199,7 @@
     private SIMRecords mSimRecords;
 
     // For non-persisted manual network selection
-    private String mManualNetworkSelectionPlmn = "";
+    private String mManualNetworkSelectionPlmn;
 
     //Common
     // Instance Variables
@@ -239,6 +240,17 @@
         }
     }
 
+    /**
+     * Used to create ImsManager instances, which may be injected during testing.
+     */
+    @VisibleForTesting
+    public interface ImsManagerFactory {
+        /**
+         * Create a new instance of ImsManager for the specified phoneId.
+         */
+        ImsManager create(Context context, int phoneId);
+    }
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private IccSmsInterfaceManager mIccSmsInterfaceManager;
 
@@ -251,6 +263,8 @@
 
     private final SettingsObserver mSettingsObserver;
 
+    private final ImsManagerFactory mImsManagerFactory;
+
     // Constructors
 
     public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId,
@@ -261,12 +275,23 @@
     public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
                         boolean unitTestMode, int phoneId, int precisePhoneType,
                         TelephonyComponentFactory telephonyComponentFactory) {
+        this(context, ci, notifier,
+                unitTestMode, phoneId, precisePhoneType,
+                telephonyComponentFactory,
+                ImsManager::getInstance);
+    }
+
+    public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
+            boolean unitTestMode, int phoneId, int precisePhoneType,
+            TelephonyComponentFactory telephonyComponentFactory,
+            ImsManagerFactory imsManagerFactory) {
         super(precisePhoneType == PhoneConstants.PHONE_TYPE_GSM ? "GSM" : "CDMA",
                 notifier, context, ci, unitTestMode, phoneId, telephonyComponentFactory);
 
         // phone type needs to be set before other initialization as other objects rely on it
         mPrecisePhoneType = precisePhoneType;
         mVoiceCallSessionStats = new VoiceCallSessionStats(mPhoneId, this);
+        mImsManagerFactory = imsManagerFactory;
         initOnce(ci);
         initRatSpecific(precisePhoneType);
         // CarrierSignalAgent uses CarrierActionAgent in construction so it needs to be created
@@ -281,7 +306,7 @@
                 .makeServiceStateTracker(this, this.mCi);
         mEmergencyNumberTracker = mTelephonyComponentFactory
                 .inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker(
-                this, this.mCi);
+                        this, this.mCi);
         mDataEnabledSettings = mTelephonyComponentFactory
                 .inject(DataEnabledSettings.class.getName()).makeDataEnabledSettings(this);
         mDeviceStateMonitor = mTelephonyComponentFactory.inject(DeviceStateMonitor.class.getName())
@@ -322,6 +347,10 @@
         SubscriptionController.getInstance().registerForUiccAppsEnabled(this,
                 EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED, null, false);
 
+        mLinkBandwidthEstimator = mTelephonyComponentFactory
+                .inject(LinkBandwidthEstimator.class.getName())
+                .makeLinkBandwidthEstimator(this);
+
         loadTtyMode();
 
         CallManager.getInstance().registerPhone(this);
@@ -1651,6 +1680,7 @@
             boolean isSelectedPhoneForEmergencyCall, boolean forceApply, int reason) {
         mSST.setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall,
                 forceApply, reason);
+
     }
 
     private void storeVoiceMailNumber(String number) {
@@ -1859,7 +1889,7 @@
 
     @Override
     public void deleteCarrierInfoForImsiEncryption() {
-        CarrierInfoManager.deleteCarrierInfoForImsiEncryption(mContext);
+        CarrierInfoManager.deleteCarrierInfoForImsiEncryption(mContext, getSubId());
     }
 
     @Override
@@ -2018,7 +2048,7 @@
             mManualNetworkSelectionPlmn = nsm.operatorNumeric;
         } else {
         //on Phone0 in emergency mode (no SIM), or in some races then clear the cache
-            mManualNetworkSelectionPlmn = "";
+            mManualNetworkSelectionPlmn = null;
             Rlog.e(LOG_TAG, "Cannot update network selection due to invalid subId "
                     + subId);
         }
@@ -4552,6 +4582,27 @@
         return Collections.emptyList();
     }
 
+    /**
+     * @return Currently bound data service package names.
+     */
+    public @NonNull List<String> getDataServicePackages() {
+        List<String> packages = new ArrayList<>();
+        int[] transports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                AccessNetworkConstants.TRANSPORT_TYPE_WLAN};
+
+        for (int transport : transports) {
+            DcTracker dct = getDcTracker(transport);
+            if (dct != null) {
+                String pkg = dct.getDataServiceManager().getDataServicePackageName();
+                if (!TextUtils.isEmpty(pkg)) {
+                    packages.add(pkg);
+                }
+            }
+        }
+
+        return packages;
+    }
+
     private void updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged(
             PersistableBundle config) {
         if (config == null) {
@@ -4616,4 +4667,18 @@
                 loge("Invalid cdma_roaming_mode settings: " + config_cdma_roaming_mode);
         }
     }
+
+    /**
+     * Determines if IMS is enabled for call.
+     *
+     * @return {@code true} if IMS calling is enabled.
+     */
+    public boolean isImsUseEnabled() {
+        ImsManager imsManager = mImsManagerFactory.create(mContext, mPhoneId);
+        boolean imsUseEnabled = ((imsManager.isVolteEnabledByPlatform()
+                && imsManager.isEnhanced4gLteModeSettingEnabledByUser())
+                || (imsManager.isWfcEnabledByPlatform() && imsManager.isWfcEnabledByUser())
+                && imsManager.isNonTtyOrTtyOnVolteEnabled());
+        return imsUseEnabled;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index a1a9578..bb21281 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -21,6 +21,7 @@
 import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD;
 
 import android.Manifest;
+import android.annotation.RequiresPermission;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -537,7 +538,7 @@
                     + " text='" + text + "' sentIntent=" + sentIntent + " deliveryIntent="
                     + deliveryIntent + " priority=" + priority + " expectMore=" + expectMore
                     + " validityPeriod=" + validityPeriod + " isForVVM=" + isForVvm
-                    + " id= " +  messageId);
+                    + " " + SmsController.formatCrossStackMessageId(messageId));
         }
         notifyIfOutgoingEmergencySms(destAddr);
         destAddr = filterDestAddress(destAddr);
@@ -743,7 +744,7 @@
             for (String part : parts) {
                 log("sendMultipartTextWithOptions: destAddr=" + destAddr + ", srAddr=" + scAddr
                         + ", part[" + (i++) + "]=" + part
-                        + " id: " + messageId);
+                        + " " + SmsController.formatCrossStackMessageId(messageId));
             }
         }
         notifyIfOutgoingEmergencySms(destAddr);
@@ -1049,8 +1050,9 @@
     /**
      * Reset all cell broadcast ranges. Previously enabled ranges will become invalid after this.
      */
+    @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
     public void resetAllCellBroadcastRanges() {
-        mContext.enforceCallingPermission(android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
+        mContext.enforceCallingPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS,
                 "resetAllCellBroadcastRanges");
         mCdmaBroadcastRangeManager.clearRanges();
         mCellBroadcastRangeManager.clearRanges();
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 8f98a5f..f4aba4f 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony;
 
+import static android.os.PowerWhitelistManager.REASON_EVENT_MMS;
+import static android.os.PowerWhitelistManager.REASON_EVENT_SMS;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.provider.Telephony.Sms.Intents.RESULT_SMS_DATABASE_ERROR;
 import static android.provider.Telephony.Sms.Intents.RESULT_SMS_DISPATCH_FAILURE;
 import static android.provider.Telephony.Sms.Intents.RESULT_SMS_INVALID_URI;
@@ -206,8 +209,10 @@
 
     // The notitfication tag used when showing a notification. The combination of notification tag
     // and notification id should be unique within the phone app.
-    private static final String NOTIFICATION_TAG = "InboundSmsHandler";
-    private static final int NOTIFICATION_ID_NEW_MESSAGE = 1;
+    @VisibleForTesting
+    public static final String NOTIFICATION_TAG = "InboundSmsHandler";
+    @VisibleForTesting
+    public static final int NOTIFICATION_ID_NEW_MESSAGE = 1;
 
     /** URI for raw table of SMS provider. */
     protected static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
@@ -676,7 +681,7 @@
      * This method is called when a new SMS PDU is injected into application framework.
      * @param ar is the AsyncResult that has the SMS PDU to be injected.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void handleInjectSms(AsyncResult ar, boolean isOverIms) {
         int result;
         SmsDispatchersController.SmsInjectionCallback callback = null;
@@ -806,7 +811,7 @@
      * @param smsSource the source of the SMS message
      * @return {@link Intents#RESULT_SMS_HANDLED} if the message was accepted, or an error status
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected int dispatchNormalMessage(SmsMessageBase sms, @SmsSource int smsSource) {
         SmsHeader smsHeader = sms.getUserDataHeader();
         InboundSmsTracker tracker;
@@ -987,8 +992,8 @@
                 log("processMessagePart: all " + messageCount + " segments "
                         + " received. refNumber: " + refNumber, tracker.getMessageId());
             } catch (SQLException e) {
-                loge("processMessagePart: Can't access multipart SMS database, id: "
-                        + tracker.getMessageId(), e);
+                loge("processMessagePart: Can't access multipart SMS database, "
+                        + SmsController.formatCrossStackMessageId(tracker.getMessageId()), e);
                 return false;
             } finally {
                 if (cursor != null) {
@@ -1043,7 +1048,8 @@
                     tracker,
                     (isWapPush ? new byte[][] {output.toByteArray()} : pdus),
                     destPort,
-                    resultReceiver);
+                    resultReceiver,
+                    block);
         }
 
         if (isWapPush) {
@@ -1116,7 +1122,7 @@
      * @return true if an ordered broadcast was sent to the carrier app; false otherwise.
      */
     private boolean processMessagePartWithUserLocked(InboundSmsTracker tracker,
-            byte[][] pdus, int destPort, SmsBroadcastReceiver resultReceiver) {
+            byte[][] pdus, int destPort, SmsBroadcastReceiver resultReceiver, boolean block) {
         if (destPort == SmsHeader.PORT_WAP_PUSH && mWapPush.isWapPushForMms(pdus[0], this)) {
             showNewMessageNotification();
             return false;
@@ -1125,14 +1131,15 @@
             // This is a regular SMS - hand it to the carrier or system app for filtering.
             boolean filterInvoked = filterSms(
                     pdus, destPort, tracker, resultReceiver, false /* userUnlocked */,
-                    false /* block */);
+                    block);
             if (filterInvoked) {
                 // filter invoked, wait for it to return the result.
                 return true;
-            } else {
-                // filter not invoked, show the notification and do nothing further.
+            } else if (!block) {
+                // filter not invoked and message not blocked, show the notification and do nothing
+                // further. Even if the message is blocked, we keep it in the database so it can be
+                // reprocessed by filters once credential-encrypted storage is available.
                 showNewMessageNotification();
-                return false;
             }
         }
         return false;
@@ -1385,9 +1392,12 @@
             bundle = bopts.toBundle();
         }
         long duration = mPowerWhitelistManager.whitelistAppTemporarilyForEvent(
-                pkgName, PowerWhitelistManager.EVENT_SMS, reason);
+                pkgName, PowerWhitelistManager.EVENT_SMS, REASON_EVENT_SMS, reason);
         if (bopts == null) bopts = BroadcastOptions.makeBasic();
-        bopts.setTemporaryAppWhitelistDuration(duration);
+        bopts.setTemporaryAppAllowlist(duration,
+                TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                REASON_EVENT_SMS,
+                "");
         bundle = bopts.toBundle();
 
         return bundle;
@@ -1560,8 +1570,8 @@
                     return Intents.RESULT_SMS_DUPLICATED;   // reject message
                 }
             } catch (SQLException e) {
-                loge("addTrackerToRawTable: Can't access SMS database, id: "
-                        + tracker.getMessageId(), e);
+                loge("addTrackerToRawTable: Can't access SMS database, "
+                        + SmsController.formatCrossStackMessageId(tracker.getMessageId()), e);
                 return RESULT_SMS_DATABASE_ERROR;    // reject message
             }
         } else {
@@ -1592,8 +1602,8 @@
             }
             return Intents.RESULT_SMS_HANDLED;
         } catch (Exception e) {
-            loge("addTrackerToRawTable: error parsing URI for new row: " + newUri + " id: "
-                    + tracker.getMessageId(), e);
+            loge("addTrackerToRawTable: error parsing URI for new row: " + newUri
+                    + " " + SmsController.formatCrossStackMessageId(tracker.getMessageId()), e);
             return RESULT_SMS_INVALID_URI;
         }
     }
@@ -1651,9 +1661,13 @@
                 long duration = mPowerWhitelistManager.whitelistAppTemporarilyForEvent(
                         mContext.getPackageName(),
                         PowerWhitelistManager.EVENT_MMS,
+                        REASON_EVENT_MMS,
                         "mms-broadcast");
                 BroadcastOptions bopts = BroadcastOptions.makeBasic();
-                bopts.setTemporaryAppWhitelistDuration(duration);
+                bopts.setTemporaryAppAllowlist(duration,
+                        TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                        REASON_EVENT_MMS,
+                        "");
                 Bundle options = bopts.toBundle();
 
                 String mimeType = intent.getType();
@@ -1726,7 +1740,7 @@
 
         @Override
         public void onFilterComplete(int result) {
-            log("onFilterComplete: result is " + result, mMessageId);
+            log("onFilterComplete: result is " + result, mTracker.getMessageId());
 
             boolean carrierRequestedDrop =
                     (result & CarrierMessagingService.RECEIVE_OPTIONS_DROP) != 0;
@@ -1745,12 +1759,18 @@
             }
 
             // Now that all filters have been invoked, drop the message if it is blocked.
-            // TODO(b/156910035): Remove mUserUnlocked once we stop showing the new message
-            // notification for blocked numbers.
-            if (mUserUnlocked && mBlock) {
-                log("onFilterComplete: dropping message as the sender is blocked",
-                        mTracker.getMessageId());
-                dropFilteredSms(mTracker, mSmsBroadcastReceiver, mBlock);
+            if (mBlock) {
+                // Only delete the message if the user is unlocked. Otherwise, we should reprocess
+                // the message after unlock so the filter has a chance to run while credential-
+                // encrypted storage is available.
+                if (mUserUnlocked) {
+                    log("onFilterComplete: dropping message as the sender is blocked",
+                            mTracker.getMessageId());
+                    dropFilteredSms(mTracker, mSmsBroadcastReceiver, mBlock);
+                } else {
+                    // Just complete handling of the message without dropping it.
+                    sendMessage(EVENT_BROADCAST_COMPLETE);
+                }
                 return;
             }
 
@@ -1802,7 +1822,7 @@
      */
     protected void logWithLocalLog(String logMsg, long id) {
         log(logMsg, id);
-        mLocalLog.log(logMsg + ", id: " + id);
+        mLocalLog.log(logMsg + ", " + SmsController.formatCrossStackMessageId(id));
     }
 
     /**
@@ -1821,7 +1841,7 @@
      */
     protected void logeWithLocalLog(String logMsg, long id) {
         loge(logMsg, id);
-        mLocalLog.log(logMsg + ", id: " + id);
+        mLocalLog.log(logMsg + ", " + SmsController.formatCrossStackMessageId(id));
     }
 
     /**
@@ -1840,7 +1860,7 @@
      * @param id unique message id
      */
     protected void log(String s, long id) {
-        log(s + ", id: " + id);
+        log(s + ", " + SmsController.formatCrossStackMessageId(id));
     }
 
     /**
@@ -1859,7 +1879,7 @@
      * @param id unique message id
      */
     protected void loge(String s, long id) {
-        loge(s + ", id: " + id);
+        loge(s + ", " + SmsController.formatCrossStackMessageId(id));
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/InboundSmsTracker.java b/src/java/com/android/internal/telephony/InboundSmsTracker.java
index 9ddee8d..9b67612 100644
--- a/src/java/com/android/internal/telephony/InboundSmsTracker.java
+++ b/src/java/com/android/internal/telephony/InboundSmsTracker.java
@@ -326,9 +326,9 @@
             builder.append(") deleteArgs=(").append(Arrays.toString(mDeleteWhereArgs));
             builder.append(')');
         }
-        builder.append(" id=");
-        builder.append(mMessageId);
-        builder.append('}');
+        builder.append(" ");
+        builder.append(SmsController.formatCrossStackMessageId(mMessageId));
+        builder.append("}");
         return builder.toString();
     }
 
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index 89778e6..1f7ad17 100755
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -86,10 +86,12 @@
      * <p> This broadcast is not effective on user build.
      *
      * <p>Example: To override the current country <code>
+     * adb root
      * adb shell am broadcast -a com.android.internal.telephony.action.COUNTRY_OVERRIDE
      * --es country us </code>
      *
      * <p> To remove the override <code>
+     * adb root
      * adb shell am broadcast -a com.android.internal.telephony.action.COUNTRY_OVERRIDE
      * --ez reset true</code>
      */
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index dc32ba5..2f5e8b0 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -699,8 +699,9 @@
         if (mPrimarySubList.size() == 1 && change == PRIMARY_SUB_REMOVED
                 && (!dataSelected || !smsSelected || !voiceSelected)) {
             dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
-        } else if (mPrimarySubList.size() > 1 && isUserVisibleChange(change)) {
-            // If change is SWAPPED_IN_GROUP or MARKED_OPPT orINITIALIZED, don't ask user again.
+        } else if (mPrimarySubList.size() > 1 && (isUserVisibleChange(change)
+                || (change == PRIMARY_SUB_INITIALIZED && !dataSelected))) {
+            // If change is SWAPPED_IN_GROUP or MARKED_OPPT, don't ask user again.
             dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
         }
 
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
index ed0fc3e..92f552a 100644
--- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -445,7 +445,10 @@
                     .setCallingPackage(nsri.mCallingPackage)
                     .setCallingPid(nsri.mPid)
                     .setCallingUid(nsri.mUid)
+                    .setCallingFeatureId(nsri.mPhone.getContext().getAttributionTag())
                     .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+                    .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
+                    .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
                     .setMethod("NetworkScanTracker#onResult")
                     .build();
             if (ar.exception == null && ar.result != null) {
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index 2bb7e66..210f485 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -275,7 +275,7 @@
                 if (kv[1].equals(ICON_5G)) {
                     icon = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA;
                 } else if (kv[1].equals(ICON_5G_PLUS)) {
-                    icon = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE;
+                    icon = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED;
                 } else {
                     if (DBG) loge("Invalid 5G icon = " + kv[1]);
                 }
@@ -367,7 +367,7 @@
         // NR display is not accurate when physical channel config notifications are off
         if (mIsPhysicalChannelConfigOn && (nrNsa || nrSa)) {
             // Process NR display network type
-            displayNetworkType = getNrDisplayType();
+            displayNetworkType = getNrDisplayType(nrSa);
             if (displayNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE) {
                 // Use LTE values if 5G values aren't defined
                 displayNetworkType = getLteDisplayType();
@@ -379,7 +379,7 @@
         return displayNetworkType;
     }
 
-    private @Annotation.OverrideNetworkType int getNrDisplayType() {
+    private @Annotation.OverrideNetworkType int getNrDisplayType(boolean isNrSa) {
         // Don't show 5G icon if preferred network type does not include 5G
         if ((mPhone.getCachedAllowedNetworkTypesBitmask()
                 & TelephonyManager.NETWORK_TYPE_BITMASK_NR) == 0) {
@@ -387,21 +387,24 @@
         }
         // Icon display keys in order of priority
         List<String> keys = new ArrayList<>();
-        // TODO: Update for NR SA
-        switch (mPhone.getServiceState().getNrState()) {
-            case NetworkRegistrationInfo.NR_STATE_CONNECTED:
-                if (isNrMmwave()) {
-                    keys.add(STATE_CONNECTED_MMWAVE);
-                }
-                keys.add(STATE_CONNECTED);
-                break;
-            case NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED:
-                keys.add(isPhysicalLinkActive() ? STATE_NOT_RESTRICTED_RRC_CON
-                        : STATE_NOT_RESTRICTED_RRC_IDLE);
-                break;
-            case NetworkRegistrationInfo.NR_STATE_RESTRICTED:
-                keys.add(STATE_RESTRICTED);
-                break;
+        if (isNrSa && isNrMmwave()) {
+            keys.add(STATE_CONNECTED_MMWAVE);
+        } else {
+            switch (mPhone.getServiceState().getNrState()) {
+                case NetworkRegistrationInfo.NR_STATE_CONNECTED:
+                    if (isNrMmwave()) {
+                        keys.add(STATE_CONNECTED_MMWAVE);
+                    }
+                    keys.add(STATE_CONNECTED);
+                    break;
+                case NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED:
+                    keys.add(isPhysicalLinkActive() ? STATE_NOT_RESTRICTED_RRC_CON
+                            : STATE_NOT_RESTRICTED_RRC_IDLE);
+                    break;
+                case NetworkRegistrationInfo.NR_STATE_RESTRICTED:
+                    keys.add(STATE_RESTRICTED);
+                    break;
+            }
         }
 
         for (String key : keys) {
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 39846b9..9d7444d 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -78,6 +78,7 @@
 import com.android.internal.telephony.dataconnection.DataConnectionReasons;
 import com.android.internal.telephony.dataconnection.DataEnabledSettings;
 import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
 import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
@@ -457,6 +458,8 @@
     protected VoiceCallSessionStats mVoiceCallSessionStats;
     protected SmsStats mSmsStats;
 
+    protected LinkBandwidthEstimator mLinkBandwidthEstimator;
+
     public IccRecords getIccRecords() {
         return mIccRecords.get();
     }
@@ -2026,7 +2029,7 @@
      *  Retrieves manually selected network info.
      */
     public String getManualNetworkSelectionPlmn() {
-        return "";
+        return null;
     }
 
 
@@ -2695,6 +2698,18 @@
         mCi.nvResetConfig(2 /* erase NV */, response);
     }
 
+    /**
+     * Erase data saved in the SharedPreference. Used for network reset
+     *
+     */
+    public boolean eraseDataInSharedPreferences() {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+        SharedPreferences.Editor editor = sp.edit();
+        Rlog.d(LOG_TAG, "Erase all data saved in SharedPreferences");
+        editor.clear();
+        return editor.commit();
+    }
+
     public void setSystemSelectionChannels(List<RadioAccessSpecifier> specifiers,
             Message response) {
         mCi.setSystemSelectionChannels(specifiers, response);
@@ -4366,20 +4381,6 @@
     }
 
     /**
-     * Determines if  IMS is enabled for call.
-     *
-     * @return {@code true} if IMS calling is enabled.
-     */
-    public boolean isImsUseEnabled() {
-        ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
-        boolean imsUseEnabled = ((imsManager.isVolteEnabledByPlatform()
-                && imsManager.isEnhanced4gLteModeSettingEnabledByUser())
-                || (imsManager.isWfcEnabledByPlatform() && imsManager.isWfcEnabledByUser())
-                && imsManager.isNonTtyOrTtyOnVolteEnabled());
-        return imsUseEnabled;
-    }
-
-    /**
      * Determines if the connection to IMS services are available yet.
      * @return {@code true} if the connection to IMS services are available.
      */
@@ -4759,6 +4760,29 @@
         return Collections.emptyList();
     }
 
+    /**
+     *
+     * @return
+     */
+    public @NonNull List<String> getDataServicePackages() {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Return link bandwidth estimator
+     */
+    public LinkBandwidthEstimator getLinkBandwidthEstimator() {
+        return mLinkBandwidthEstimator;
+    }
+
+    /**
+     * Request to get the current slicing configuration including URSP rules and
+     * NSSAIs (configured, allowed and rejected).
+     */
+    public void getSlicingConfig(Message response) {
+        mCi.getSlicingConfig(response);
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Phone: subId=" + getSubId());
         pw.println(" mPhoneId=" + mPhoneId);
@@ -4935,6 +4959,12 @@
             pw.println("++++++++++++++++++++++++++++++++");
         }
 
+        if (getLinkBandwidthEstimator() != null) {
+            pw.println("LinkBandwidthEstimator:");
+            getLinkBandwidthEstimator().dump(fd, pw, args);
+            pw.println("++++++++++++++++++++++++++++++++");
+        }
+
         pw.println("Phone Local Log: ");
         if (mLocalLog != null) {
             try {
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index 907d3e6..d92f96d 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -312,7 +312,7 @@
     }
 
     public int getNumberOfModemsWithSimultaneousDataConnections() {
-        return mStaticCapability.maxActiveData;
+        return mStaticCapability.getMaxActiveDataSubscriptions();
     }
 
     private void notifyCapabilityChanged() {
@@ -327,7 +327,7 @@
      */
     public void switchMultiSimConfig(int numOfSims) {
         log("switchMultiSimConfig: with numOfSims = " + numOfSims);
-        if (getStaticPhoneCapability().logicalModemList.size() < numOfSims) {
+        if (getStaticPhoneCapability().getLogicalModemList().size() < numOfSims) {
             log("switchMultiSimConfig: Phone is not capable of enabling "
                     + numOfSims + " sims, exiting!");
             return;
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index a480af8..d51fa91 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -177,7 +177,6 @@
                             cdmaSubscription, i);
                 }
 
-
                 if (numPhones > 0) {
                     final RadioConfig radioConfig = RadioConfig.make(context,
                             sCommandsInterfaces[0].getHalVersion());
@@ -190,6 +189,7 @@
                             radioConfig, null);
                 }
 
+
                 // Instantiate UiccController so that all other classes can just
                 // call getInstance()
                 sUiccController = UiccController.make(context);
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index 4378529..701a157 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -124,7 +124,6 @@
     void notifyPhysicalChannelConfig(Phone sender, List<PhysicalChannelConfig> configs);
 
     /** Notify DataEnabled has changed. */
-
     void notifyDataEnabled(Phone sender, boolean enabled, @DataEnabledReason int reason);
 
     /** Notify Allowed Network Type has changed. */
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index 8211f2d..2727a19 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -264,6 +264,17 @@
         return PhoneFactory.getPhone(phoneId);
     }
 
+    private boolean enforceIccSimChallengeResponsePermission(Context context, int subId,
+            String callingPackage, String callingFeatureId, String message) {
+        if (TelephonyPermissions.checkCallingOrSelfUseIccAuthWithDeviceIdentifier(context,
+                callingPackage, callingFeatureId, message)) {
+            return true;
+        }
+        if (VDBG) log("No USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER permission.");
+        enforcePrivilegedPermissionOrCarrierPrivilege(subId, message);
+        return true;
+    }
+
     /**
      * Make sure caller has either read privileged phone permission or carrier privilege.
      *
@@ -370,8 +381,9 @@
                 });
     }
 
-    public String getIccSimChallengeResponse(int subId, int appType, int authType, String data)
-            throws RemoteException {
+    @Override
+    public String getIccSimChallengeResponse(int subId, int appType, int authType, String data,
+            String callingPackage, String callingFeatureId) throws RemoteException {
         CallPhoneMethodHelper<String> toExecute = (phone)-> {
             UiccCard uiccCard = phone.getUiccCard();
             if (uiccCard == null) {
@@ -396,12 +408,9 @@
             return uiccApp.getIccRecords().getIccSimChallengeResponse(authType, data);
         };
 
-        return callPhoneMethodWithPermissionCheck(subId, null, null, "getIccSimChallengeResponse",
-                toExecute,
-                (aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage) -> {
-                    enforcePrivilegedPermissionOrCarrierPrivilege(aSubId, aMessage);
-                    return true;
-                });
+        return callPhoneMethodWithPermissionCheck(subId, callingPackage, callingFeatureId,
+                "getIccSimChallengeResponse", toExecute,
+                this::enforceIccSimChallengeResponsePermission);
     }
 
     public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage,
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index 46bf008..73859ef 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -24,6 +24,8 @@
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION;
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
 
 import static java.util.Arrays.copyOf;
 
@@ -56,8 +58,10 @@
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyRegistryManager;
 import android.telephony.data.ApnSetting;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.LocalLog;
 
+import com.android.ims.ImsManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.SubscriptionController.WatchedInt;
 import com.android.internal.telephony.dataconnection.ApnConfigTypeRepository;
@@ -309,6 +313,19 @@
     private final DefaultNetworkCallback mDefaultNetworkCallback = new DefaultNetworkCallback();
 
     /**
+     * Interface to get ImsRegistrationTech. It's a wrapper of ImsManager#getRegistrationTech,
+     * to make it mock-able in unittests.
+     */
+    public interface ImsRegTechProvider {
+        /** Get IMS registration tech. */
+        @ImsRegistrationImplBase.ImsRegistrationTech int get(Context context, int phoneId);
+    }
+
+    @VisibleForTesting
+    public ImsRegTechProvider mImsRegTechProvider =
+            (context, phoneId) -> ImsManager.getInstance(context, phoneId).getRegistrationTech();
+
+    /**
      * Method to get singleton instance.
      */
     public static PhoneSwitcher getInstance() {
@@ -327,6 +344,27 @@
         return sPhoneSwitcher;
     }
 
+    /**
+     * Whether this phone IMS registration is on its original network. This result impacts
+     * whether we want to do DDS switch to the phone having voice call.
+     * If it's registered on IWLAN or cross SIM in multi-SIM case, return false. Otherwise,
+     * return true.
+     */
+    private boolean isImsOnOriginalNetwork(Phone phone) {
+        if (phone == null) return false;
+        int phoneId = phone.getPhoneId();
+        if (!SubscriptionManager.isValidPhoneId(phoneId)) return false;
+
+        int imsRegTech = mImsRegTechProvider.get(mContext, phoneId);
+        // If IMS is registered on IWLAN or cross SIM, return false.
+        boolean isOnOriginalNetwork = (imsRegTech != REGISTRATION_TECH_IWLAN)
+                && (imsRegTech != REGISTRATION_TECH_CROSS_SIM);
+        if (!isOnOriginalNetwork) {
+            log("IMS call on IWLAN or cross SIM. Call will be ignored for DDS switch");
+        }
+        return isOnOriginalNetwork;
+    }
+
     private boolean isPhoneInVoiceCallChanged() {
         int oldPhoneIdInVoiceCall = mPhoneIdInVoiceCall;
         // If there's no active call, the value will become INVALID_PHONE_INDEX
@@ -334,7 +372,8 @@
         // subscription.
         mPhoneIdInVoiceCall = SubscriptionManager.INVALID_PHONE_INDEX;
         for (Phone phone : PhoneFactory.getPhones()) {
-            if (isPhoneInVoiceCall(phone) || isPhoneInVoiceCall(phone.getImsPhone())) {
+            if (isPhoneInVoiceCall(phone) || (isPhoneInVoiceCall(phone.getImsPhone())
+                    && isImsOnOriginalNetwork(phone))) {
                 mPhoneIdInVoiceCall = phone.getPhoneId();
                 break;
             }
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 638ff39..53f876c 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -109,9 +109,9 @@
 import android.telephony.data.DataCallResponse.HandoverFailureMode;
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
+import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.Qos;
 import android.telephony.data.QosBearerSession;
-import android.telephony.data.SliceInfo;
 import android.telephony.data.TrafficDescriptor;
 import android.telephony.emergency.EmergencyNumber;
 import android.text.TextUtils;
@@ -430,7 +430,7 @@
         switch(rr.mRequest) {
             case RIL_REQUEST_GET_ACTIVITY_INFO:
                 timeoutResponse = new ModemActivityInfo(
-                        0, 0, 0, new int [ModemActivityInfo.TX_POWER_LEVELS], 0);
+                        0, 0, 0, new int [ModemActivityInfo.getNumTxPowerLevels()], 0);
                 break;
         };
         return timeoutResponse;
@@ -1804,7 +1804,8 @@
         }
         long messageId = ((SMSDispatcher.SmsTracker) result.obj).mMessageId;
         if (RILJ_LOGV) {
-            Rlog.d(RILJ_LOG_TAG, "getOutgoingSmsMessageId messageId: " + messageId);
+            Rlog.d(RILJ_LOG_TAG, "getOutgoingSmsMessageId "
+                    + SmsController.formatCrossStackMessageId(messageId));
         }
         return messageId;
     }
@@ -1930,7 +1931,7 @@
         return dpi;
     }
 
-    private static OptionalSliceInfo convertToHalSliceInfo(@Nullable SliceInfo sliceInfo) {
+    private static OptionalSliceInfo convertToHalSliceInfo(@Nullable NetworkSliceInfo sliceInfo) {
         OptionalSliceInfo optionalSliceInfo = new OptionalSliceInfo();
         if (sliceInfo == null) {
             return optionalSliceInfo;
@@ -1956,8 +1957,8 @@
                 new android.hardware.radio.V1_6.TrafficDescriptor();
 
         OptionalDnn optionalDnn = new OptionalDnn();
-        if (trafficDescriptor.getDnn() != null) {
-            optionalDnn.value(trafficDescriptor.getDnn());
+        if (trafficDescriptor.getDataNetworkName() != null) {
+            optionalDnn.value(trafficDescriptor.getDataNetworkName());
         }
         td.dnn = optionalDnn;
 
@@ -2051,8 +2052,8 @@
     @Override
     public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
             boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
-            Message result) {
+            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+            boolean matchAllRuleAllowed, Message result) {
         IRadio radioProxy = getRadioProxy(result);
 
         if (radioProxy != null) {
@@ -5572,8 +5573,7 @@
                 } catch (RemoteException | RuntimeException e) {
                     handleRadioProxyExceptionForRR(rr, "setCarrierInfoForImsiEncryption", e);
                 }
-            }
-            else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+            } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
                 android.hardware.radio.V1_1.IRadio radioProxy11 =
                         (android.hardware.radio.V1_1.IRadio ) radioProxy;
 
@@ -5977,6 +5977,32 @@
         }
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void getSlicingConfig(Message result) {
+        android.hardware.radio.V1_6.IRadio radioProxy16 = getRadioV16(result);
+
+        if (radioProxy16 != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLICING_CONFIG, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy16.getSlicingConfig(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "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();
+        }
+    }
+
     //***** Private Methods
     /** Helper that gets V1.6 of the radio interface OR sends back REQUEST_NOT_SUPPORTED */
     @Nullable private android.hardware.radio.V1_6.IRadio getRadioV16(Message msg) {
@@ -6954,6 +6980,8 @@
                 return "RIL_REQUEST_SET_ALLOWED_NETWORK_TYPE_BITMAP";
             case RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP:
                 return "RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP";
+            case RIL_REQUEST_GET_SLICING_CONFIG:
+                return "RIL_REQUEST_GET_SLICING_CONFIG";
             default: return "<unknown request>";
         }
     }
@@ -7490,7 +7518,7 @@
 
         List<LinkAddress> laList = new ArrayList<>();
         List<QosBearerSession> qosSessions = new ArrayList<>();
-        SliceInfo sliceInfo = null;
+        NetworkSliceInfo sliceInfo = null;
         List<TrafficDescriptor> trafficDescriptors = new ArrayList<>();
 
         if (dcResult instanceof android.hardware.radio.V1_0.SetupDataCallResult) {
@@ -7657,17 +7685,17 @@
                 .build();
     }
 
-    private static SliceInfo convertToSliceInfo(OptionalSliceInfo optionalSliceInfo) {
+    private static NetworkSliceInfo convertToSliceInfo(OptionalSliceInfo optionalSliceInfo) {
         if (optionalSliceInfo.getDiscriminator() == OptionalSliceInfo.hidl_discriminator.noinit) {
             return null;
         }
 
         android.hardware.radio.V1_6.SliceInfo si = optionalSliceInfo.value();
-        SliceInfo.Builder builder =
-                new SliceInfo.Builder()
+        NetworkSliceInfo.Builder builder =
+                new NetworkSliceInfo.Builder()
                 .setSliceServiceType(si.sst)
                 .setMappedHplmnSliceServiceType(si.mappedHplmnSst);
-        if (si.sliceDifferentiator != SliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) {
+        if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) {
             builder
                 .setSliceDifferentiator(si.sliceDifferentiator)
                 .setMappedHplmnSliceDifferentiator(si.mappedHplmnSD);
@@ -7681,7 +7709,14 @@
                 ? null : td.dnn.value();
         String osAppId = td.osAppId.getDiscriminator() == OptionalOsAppId.hidl_discriminator.noinit
                 ? null : new String(arrayListToPrimitiveArray(td.osAppId.value().osAppId));
-        return new TrafficDescriptor(dnn, osAppId);
+        TrafficDescriptor.Builder builder = new TrafficDescriptor.Builder();
+        if (dnn != null) {
+            builder.setDataNetworkName(dnn);
+        }
+        if (osAppId != null) {
+            builder.setOsAppId(osAppId);
+        }
+        return builder.build();
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
index 7a1e089..32b24fe 100644
--- a/src/java/com/android/internal/telephony/RadioConfig.java
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.PhoneCapability.DEVICE_NR_CAPABILITY_NSA;
+import static android.telephony.PhoneCapability.DEVICE_NR_CAPABILITY_SA;
+
 import static com.android.internal.telephony.RILConstants.RADIO_NOT_AVAILABLE;
 import static com.android.internal.telephony.RILConstants.REQUEST_NOT_SUPPORTED;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES;
@@ -45,6 +48,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.concurrent.atomic.AtomicLong;
 
@@ -77,6 +81,7 @@
     private final SparseArray<RILRequest> mRequestList = new SparseArray<RILRequest>();
     /* default work source which will blame phone process */
     private final WorkSource mDefaultWorkSource;
+    private final int[] mDeviceNrCapabilities;
     private static RadioConfig sRadioConfig;
     private static final Object sLock = new Object();
 
@@ -108,6 +113,24 @@
 
         mDefaultWorkSource = new WorkSource(context.getApplicationInfo().uid,
                 context.getPackageName());
+
+        boolean is5gStandalone = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_telephony5gStandalone);
+        boolean is5gNonStandalone = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_telephony5gNonStandalone);
+
+        if (!is5gStandalone && !is5gNonStandalone) {
+            mDeviceNrCapabilities = new int[0];
+        } else {
+            List<Integer> list = new ArrayList<>();
+            if (is5gNonStandalone) {
+                list.add(DEVICE_NR_CAPABILITY_NSA);
+            }
+            if (is5gStandalone) {
+                list.add(DEVICE_NR_CAPABILITY_SA);
+            }
+            mDeviceNrCapabilities = list.stream().mapToInt(Integer::valueOf).toArray();
+        }
     }
 
     /**
@@ -563,6 +586,13 @@
         }
     }
 
+    /**
+     * Returns the device's nr capability.
+     */
+    public int[] getDeviceNrCapabilities() {
+        return mDeviceNrCapabilities;
+    }
+
     static ArrayList<IccSlotStatus> convertHalSlotStatus(
             ArrayList<android.hardware.radio.config.V1_0.SimSlotStatus> halSlotStatusList) {
         ArrayList<IccSlotStatus> response = new ArrayList<IccSlotStatus>(halSlotStatusList.size());
diff --git a/src/java/com/android/internal/telephony/RadioConfigResponse.java b/src/java/com/android/internal/telephony/RadioConfigResponse.java
index 95c304d..88e5e80 100644
--- a/src/java/com/android/internal/telephony/RadioConfigResponse.java
+++ b/src/java/com/android/internal/telephony/RadioConfigResponse.java
@@ -20,6 +20,7 @@
 import static android.telephony.TelephonyManager
         .CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE;
 import static android.telephony.TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE;
+import static android.telephony.TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED;
 import static android.telephony.TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING;
 import static android.telephony.TelephonyManager.RadioInterfaceCapability;
 
@@ -133,7 +134,6 @@
         // TODO b/121394331: clean up V1_1.PhoneCapability fields.
         int maxActiveVoiceCalls = 0;
         int maxActiveData = phoneCapability.maxActiveData;
-        int max5G = 0;
         boolean validationBeforeSwitchSupported = phoneCapability.isInternetLingeringSupported;
         List<ModemInfo> logicalModemList = new ArrayList();
 
@@ -142,8 +142,8 @@
             logicalModemList.add(new ModemInfo(modemInfo.modemId));
         }
 
-        return new PhoneCapability(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList,
-                validationBeforeSwitchSupported);
+        return new PhoneCapability(maxActiveVoiceCalls, maxActiveData, logicalModemList,
+                validationBeforeSwitchSupported, mRadioConfig.getDeviceNrCapabilities());
     }
     /**
      * Response function for IRadioConfig.getPhoneCapability().
@@ -310,6 +310,8 @@
                 Rlog.d(TAG, "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE");
                 caps.add(CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING);
                 Rlog.d(TAG, "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING");
+                caps.add(CAPABILITY_SLICING_CONFIG_SUPPORTED);
+                Rlog.d(TAG, "CAPABILITY_SLICING_CONFIG_SUPPORTED");
             }
         }
         return caps;
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index 7dd9dfb..313ef0b 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -56,6 +56,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
+import android.telephony.data.SlicingConfig;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.dataconnection.KeepaliveStatus;
@@ -2803,16 +2804,16 @@
             if (responseInfo.error == RadioError.NONE) {
                 final int sleepModeTimeMs = activityInfo.sleepModeTimeMs;
                 final int idleModeTimeMs = activityInfo.idleModeTimeMs;
-                int [] txModeTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
-                for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+                int [] txModeTimeMs = new int[ModemActivityInfo.getNumTxPowerLevels()];
+                for (int i = 0; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
                     txModeTimeMs[i] = activityInfo.txmModetimeMs[i];
                 }
                 final int rxModeTimeMs = activityInfo.rxModeTimeMs;
                 ret = new ModemActivityInfo(SystemClock.elapsedRealtime(), sleepModeTimeMs,
                         idleModeTimeMs, txModeTimeMs, rxModeTimeMs);
             } else {
-                ret = new ModemActivityInfo(0, 0, 0, new int [ModemActivityInfo.TX_POWER_LEVELS],
-                        0);
+                ret = new ModemActivityInfo(0, 0, 0,
+                        new int[ModemActivityInfo.getNumTxPowerLevels()], 0);
                 responseInfo.error = RadioError.NONE;
             }
             sendMessageResponse(rr.mResult, ret);
@@ -3102,10 +3103,11 @@
         RILRequest rr = mRil.processResponse_1_6(info);
 
         if (rr != null) {
+            SlicingConfig ret = new SlicingConfig(slicingConfig);
             if (info.error == RadioError.NONE) {
-                sendMessageResponse(rr.mResult, slicingConfig);
+                sendMessageResponse(rr.mResult, ret);
             }
-            mRil.processResponseDone_1_6(rr, info, slicingConfig);
+            mRil.processResponseDone_1_6(rr, info, ret);
         }
     }
 }
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 6774d46..d47dda1 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -359,7 +359,8 @@
                                         SmsSenderCallback senderCallback) {
             mSenderCallback = senderCallback;
             if (!mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
-                    mContext, carrierPackageName, ()->onServiceReady())) {
+                    mContext, carrierPackageName, runnable -> runnable.run(),
+                    ()->onServiceReady())) {
                 Rlog.e(TAG, "bindService() for carrier messaging service failed");
                 mSenderCallback.onSendSmsComplete(
                         CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
@@ -394,6 +395,7 @@
                             (mTracker.mDeliveryIntent != null)
                                     ? CarrierMessagingService.SEND_FLAG_REQUEST_DELIVERY_STATUS
                                     : 0,
+                            runnable -> runnable.run(),
                             mSenderCallback);
                 } catch (RuntimeException e) {
                     Rlog.e(TAG, "Exception sending the SMS: " + e);
@@ -433,10 +435,11 @@
                             (mTracker.mDeliveryIntent != null)
                                     ? CarrierMessagingService.SEND_FLAG_REQUEST_DELIVERY_STATUS
                                     : 0,
+                            runnable -> runnable.run(),
                             mSenderCallback);
                 } catch (RuntimeException e) {
                     Rlog.e(TAG, "Exception sending the SMS: " + e
-                            + " id: " + mTracker.mMessageId);
+                            + " " + SmsController.formatCrossStackMessageId(mTracker.mMessageId));
                     mSenderCallback.onSendSmsComplete(
                             CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                             0 /* messageRef */);
@@ -468,7 +471,7 @@
             checkCallerIsPhoneOrCarrierApp();
             final long identity = Binder.clearCallingIdentity();
             try {
-                mSmsSender.mCarrierMessagingServiceWrapper.disposeConnection(mContext);
+                mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
                 processSendSmsResponse(mSmsSender.mTracker, result, messageRef);
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -481,8 +484,8 @@
         }
 
         @Override
-        public void onFilterComplete(int result) {
-            Rlog.e(TAG, "Unexpected onFilterComplete call with result: " + result);
+        public void onReceiveSmsComplete(int result) {
+            Rlog.e(TAG, "Unexpected onReceiveSmsComplete call with result: " + result);
         }
 
         @Override
@@ -509,7 +512,8 @@
         switch (result) {
             case CarrierMessagingService.SEND_STATUS_OK:
                 Rlog.d(TAG, "processSendSmsResponse: Sending SMS by CarrierMessagingService "
-                        + "succeeded. id: " + tracker.mMessageId);
+                        + "succeeded. "
+                        + SmsController.formatCrossStackMessageId(tracker.mMessageId));
                 sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE,
                                           new AsyncResult(tracker,
                                                           smsResponse,
@@ -517,19 +521,21 @@
                 break;
             case CarrierMessagingService.SEND_STATUS_ERROR:
                 Rlog.d(TAG, "processSendSmsResponse: Sending SMS by CarrierMessagingService failed."
-                        + " id: " + tracker.mMessageId);
+                        + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
                 sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE,
                         new AsyncResult(tracker, smsResponse,
                                 new CommandException(CommandException.Error.GENERIC_FAILURE))));
                 break;
             case CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK:
                 Rlog.d(TAG, "processSendSmsResponse: Sending SMS by CarrierMessagingService failed."
-                        + " Retry on carrier network. id: " + tracker.mMessageId);
+                        + " Retry on carrier network. "
+                        + SmsController.formatCrossStackMessageId(tracker.mMessageId));
                 sendSubmitPdu(tracker);
                 break;
             default:
                 Rlog.d(TAG, "processSendSmsResponse: Unknown result " + result + " Retry on carrier"
-                        + " network. id: " + tracker.mMessageId);
+                        + " network. "
+                        + SmsController.formatCrossStackMessageId(tracker.mMessageId));
                 sendSubmitPdu(tracker);
         }
     }
@@ -555,7 +561,8 @@
                                  MultipartSmsSenderCallback senderCallback) {
             mSenderCallback = senderCallback;
             if (!mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
-                    mContext, carrierPackageName, ()->onServiceReady())) {
+                    mContext, carrierPackageName, runnable -> runnable.run(),
+                    ()->onServiceReady())) {
                 Rlog.e(TAG, "bindService() for carrier messaging service failed");
                 mSenderCallback.onSendMultipartSmsComplete(
                         CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
@@ -582,6 +589,7 @@
                         statusReportRequested
                                 ? CarrierMessagingService.SEND_FLAG_REQUEST_DELIVERY_STATUS
                                 : 0,
+                        runnable -> runnable.run(),
                         mSenderCallback);
             } catch (RuntimeException e) {
                 Rlog.e(TAG, "Exception sending the SMS: " + e);
@@ -613,7 +621,7 @@
          */
         @Override
         public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
-            mSmsSender.mCarrierMessagingServiceWrapper.disposeConnection(mContext);
+            mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
 
             if (mSmsSender.mTrackers == null) {
                 Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with null trackers.");
@@ -630,8 +638,8 @@
         }
 
         @Override
-        public void onFilterComplete(int result) {
-            Rlog.e(TAG, "Unexpected onFilterComplete call with result: " + result);
+        public void onReceiveSmsComplete(int result) {
+            Rlog.e(TAG, "Unexpected onReceiveSmsComplete call with result: " + result);
         }
 
         @Override
@@ -655,7 +663,8 @@
         switch (result) {
             case CarrierMessagingService.SEND_STATUS_OK:
                 Rlog.d(TAG, "processSendMultipartSmsResponse: Sending SMS by "
-                        + "CarrierMessagingService succeeded. id: " + trackers[0].mMessageId);
+                        + "CarrierMessagingService succeeded. "
+                        + SmsController.formatCrossStackMessageId(trackers[0].mMessageId));
                 // Sending a multi-part SMS by CarrierMessagingService successfully completed.
                 // Send EVENT_SEND_SMS_COMPLETE for all the parts one by one.
                 for (int i = 0; i < trackers.length; i++) {
@@ -675,7 +684,8 @@
                 break;
             case CarrierMessagingService.SEND_STATUS_ERROR:
                 Rlog.d(TAG, "processSendMultipartSmsResponse: Sending SMS by "
-                        + "CarrierMessagingService failed. id: " + trackers[0].mMessageId);
+                        + "CarrierMessagingService failed. "
+                        + SmsController.formatCrossStackMessageId(trackers[0].mMessageId));
                 // Sending a multi-part SMS by CarrierMessagingService failed.
                 // Send EVENT_SEND_SMS_COMPLETE with GENERIC_FAILURE for all the parts one by one.
                 for (int i = 0; i < trackers.length; i++) {
@@ -696,15 +706,16 @@
                 break;
             case CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK:
                 Rlog.d(TAG, "processSendMultipartSmsResponse: Sending SMS by "
-                        + "CarrierMessagingService failed. Retry on carrier network. id: "
-                        + trackers[0].mMessageId);
+                        + "CarrierMessagingService failed. Retry on carrier network. "
+                        + SmsController.formatCrossStackMessageId(trackers[0].mMessageId));
                 // All the parts for a multi-part SMS are handled together for retry. It helps to
                 // check user confirmation once also if needed.
                 sendSubmitPdu(trackers);
                 break;
             default:
                 Rlog.d(TAG, "processSendMultipartSmsResponse: Unknown result " + result
-                        + ". Retry on carrier network. id: " + trackers[0].mMessageId);
+                        + ". Retry on carrier network. "
+                        + SmsController.formatCrossStackMessageId(trackers[0].mMessageId));
                 sendSubmitPdu(trackers);
         }
     }
@@ -754,7 +765,7 @@
         if (ar.exception == null) {
             if (DBG) {
                 Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent
-                        + " id: " + tracker.mMessageId);
+                        + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
             }
 
             if (tracker.mDeliveryIntent != null) {
@@ -773,7 +784,8 @@
                     tracker.isFromDefaultSmsApplication(mContext));
         } else {
             if (DBG) {
-                Rlog.d(TAG, "SMS send failed id: " + tracker.mMessageId);
+                Rlog.d(TAG, "SMS send failed "
+                        + SmsController.formatCrossStackMessageId(tracker.mMessageId));
             }
 
             int ss = mPhone.getServiceState().getState();
@@ -792,7 +804,7 @@
                         + " mImsRetry=" + tracker.mImsRetry
                         + " mMessageRef=" + tracker.mMessageRef
                         + " SS= " + mPhone.getServiceState().getState()
-                        + " id=" + tracker.mMessageId);
+                        + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
             }
 
             // if sms over IMS is not supported on data and voice is not available...
@@ -1721,7 +1733,7 @@
         PackageManager pm = mContext.getPackageManager();
         try {
             ApplicationInfo appInfo = pm.getApplicationInfoAsUser(appPackage, 0,
-                UserHandle.getUserHandleForUid(userId));
+                    UserHandle.of(userId));
             return appInfo.loadSafeLabel(pm);
         } catch (PackageManager.NameNotFoundException e) {
             Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage);
@@ -1846,7 +1858,7 @@
             mSmsDispatchersController.sendRetrySms(tracker);
         } else {
             Rlog.e(TAG, mSmsDispatchersController + " is null. Retry failed"
-                    + " id: " + tracker.mMessageId);
+                    + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
         }
     }
 
@@ -2142,7 +2154,7 @@
                     mSentIntent.send(context, error, fillIn);
                 } catch (CanceledException ex) {
                     Rlog.e(TAG, "Failed to send result"
-                            + " id: " + mMessageId);
+                            + " " + SmsController.formatCrossStackMessageId(mMessageId));
                 }
             }
             reportAnomaly(error, errorCode);
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index ccc8539..5807b54 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -87,6 +87,7 @@
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
 import android.telephony.VoiceSpecificRegistrationInfo;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.LocalLog;
@@ -94,6 +95,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriInfo;
@@ -239,6 +241,7 @@
     private RegistrantList mNrStateChangedRegistrants = new RegistrantList();
     private RegistrantList mNrFrequencyChangedRegistrants = new RegistrantList();
     private RegistrantList mCssIndicatorChangedRegistrants = new RegistrantList();
+    private final RegistrantList mAirplaneModeChangedRegistrants = new RegistrantList();
 
     /* Radio power off pending flag and tag counter */
     private boolean mPendingRadioPowerOffAfterDataOff = false;
@@ -428,7 +431,6 @@
             Context context = mPhone.getContext();
 
             mPhone.notifyPhoneStateChanged();
-            mPhone.notifyCallForwardingIndicator();
 
             if (!SubscriptionManager.isValidSubscriptionId(mPrevSubId)) {
                 // just went from invalid to valid subId, so notify with current service
@@ -2007,6 +2009,7 @@
     public void onAirplaneModeChanged(boolean isAirplaneModeOn) {
         mLastNitzData = null;
         mNitzState.handleAirplaneModeChanged(isAirplaneModeOn);
+        mAirplaneModeChangedRegistrants.notifyResult(isAirplaneModeOn);
     }
 
     protected Phone getPhone() {
@@ -2867,6 +2870,36 @@
             wfcFlightSpnFormat = wfcSpnFormats[flightModeIdx];
         }
 
+        String crossSimSpnFormat = null;
+        if (mPhone.getImsPhone() != null
+                && (mPhone.getImsPhone() != null)
+                && (mPhone.getImsPhone().getImsRegistrationTech()
+                == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM)) {
+            // In Cros SIM Calling mode show SPN or PLMN + Cross SIM Calling
+            //
+            // 1) Show SPN + Cross SIM Calling If SIM has SPN and SPN display condition
+            //    is satisfied or SPN override is enabled for this carrier
+            //
+            // 2) Show PLMN + Cross SIM Calling if there is no valid SPN in case 1
+            PersistableBundle bundle = getCarrierConfig();
+            int crossSimSpnFormatIdx =
+                    bundle.getInt(CarrierConfigManager.KEY_CROSS_SIM_SPN_FORMAT_INT);
+            boolean useRootLocale =
+                    bundle.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE);
+
+            String[] crossSimSpnFormats = SubscriptionManager.getResourcesForSubId(
+                    mPhone.getContext(),
+                    mPhone.getSubId(), useRootLocale)
+                    .getStringArray(R.array.crossSimSpnFormats);
+
+            if (crossSimSpnFormatIdx < 0 || crossSimSpnFormatIdx >= crossSimSpnFormats.length) {
+                loge("updateSpnDisplay: KEY_CROSS_SIM_SPN_FORMAT_INT out of bounds: "
+                        + crossSimSpnFormatIdx);
+                crossSimSpnFormatIdx = 0;
+            }
+            crossSimSpnFormat = crossSimSpnFormats[crossSimSpnFormatIdx];
+        }
+
         if (mPhone.isPhoneTypeGsm()) {
             // The values of plmn/showPlmn change in different scenarios.
             // 1) No service but emergency call allowed -> expected
@@ -2934,9 +2967,27 @@
                     && ((rule & CARRIER_NAME_DISPLAY_BITMASK_SHOW_SPN)
                     == CARRIER_NAME_DISPLAY_BITMASK_SHOW_SPN);
             if (DBG) log("updateSpnDisplay: rawSpn = " + spn);
-
-            if (!TextUtils.isEmpty(spn) && !TextUtils.isEmpty(wfcVoiceSpnFormat) &&
-                    !TextUtils.isEmpty(wfcDataSpnFormat)) {
+            if (!TextUtils.isEmpty(crossSimSpnFormat)) {
+                if (!TextUtils.isEmpty(spn)) {
+                    // Show SPN + Cross-SIM Calling If SIM has SPN and SPN display condition
+                    // is satisfied or SPN override is enabled for this carrier.
+                    String originalSpn = spn.trim();
+                    spn = String.format(crossSimSpnFormat, originalSpn);
+                    dataSpn = spn;
+                    showSpn = true;
+                    showPlmn = false;
+                } else if (!TextUtils.isEmpty(plmn)) {
+                    // Show PLMN + Cross-SIM Calling if there is no valid SPN in the above case
+                    String originalPlmn = plmn.trim();
+                    PersistableBundle config = getCarrierConfig();
+                    if (mIccRecords != null && config.getBoolean(
+                            CarrierConfigManager.KEY_WFC_CARRIER_NAME_OVERRIDE_BY_PNN_BOOL)) {
+                        originalPlmn = mIccRecords.getPnnHomeName();
+                    }
+                    plmn = String.format(crossSimSpnFormat, originalPlmn);
+                }
+            } else if (!TextUtils.isEmpty(spn) && !TextUtils.isEmpty(wfcVoiceSpnFormat)
+                    && !TextUtils.isEmpty(wfcDataSpnFormat)) {
                 // Show SPN + Wi-Fi Calling If SIM has SPN and SPN display condition
                 // is satisfied or SPN override is enabled for this carrier.
 
@@ -4745,6 +4796,27 @@
     }
 
     /**
+     * Registration for Airplane Mode changing.  The state of Airplane Mode will be returned
+     * {@link AsyncResult#result} as a {@link Boolean} Object.
+     * The {@link AsyncResult} will be in the notification {@link Message#obj}.
+     * @param h handler to notify
+     * @param what what code of message when delivered
+     * @param obj placed in {@link AsyncResult#userObj}
+     */
+    public void registerForAirplaneModeChanged(Handler h, int what, Object obj) {
+        mAirplaneModeChangedRegistrants.add(h, what, obj);
+    }
+
+    /**
+     * Unregister for Airplane Mode changed event.
+     *
+     * @param h The handler
+     */
+    public void unregisterForAirplaneModeChanged(Handler h) {
+        mAirplaneModeChangedRegistrants.remove(h);
+    }
+
+    /**
      * Registration point for transition into network attached.
      * @param h handler to notify
      * @param what what code of message when delivered
@@ -5000,6 +5072,7 @@
         updateReportingCriteria(config);
         updateOperatorNamePattern(config);
         mCdnr.updateEfFromCarrierConfig(config);
+        mPhone.notifyCallForwardingIndicator();
 
         // Sometimes the network registration information comes before carrier config is ready.
         // For some cases like roaming/non-roaming overriding, we need carrier config. So it's
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java
index 3c9fe0a..e7feaf4 100644
--- a/src/java/com/android/internal/telephony/SmsController.java
+++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -841,4 +841,11 @@
             return false;
         }
     }
+
+    /**
+     * Internal API to consistently format the debug log output of the cross-stack message id.
+     */
+    public static String formatCrossStackMessageId(long id) {
+        return "{x-message-id:" + id + "}";
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index ddaf571..2e38376 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.telephony;
 
-import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
-import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
 import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
 import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_NONE;
 import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_TEMPORARY;
@@ -788,9 +786,8 @@
             long messageId) {
         if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
             mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
-                    messageUri, callingPkg, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
-                    false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm,
-                    messageId);
+                    messageUri, callingPkg, persistMessage, priority, false /*expectMore*/,
+                    validityPeriod, isForVvm, messageId);
         } else {
             if (isCdmaMo()) {
                 mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
@@ -915,10 +912,8 @@
             long messageId) {
         if (mImsSmsDispatcher.isAvailable()) {
             mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
-                    deliveryIntents, messageUri, callingPkg, persistMessage,
-                    SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
-                    false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED,
-                    messageId);
+                    deliveryIntents, messageUri, callingPkg, persistMessage, priority,
+                    false /*expectMore*/, validityPeriod, messageId);
         } else {
             if (isCdmaMo()) {
                 mCdmaDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
diff --git a/src/java/com/android/internal/telephony/SmsResponse.java b/src/java/com/android/internal/telephony/SmsResponse.java
index 2c8afca..851d04b 100644
--- a/src/java/com/android/internal/telephony/SmsResponse.java
+++ b/src/java/com/android/internal/telephony/SmsResponse.java
@@ -59,8 +59,7 @@
         String ret = "{ mMessageRef = " + mMessageRef
                         + ", mErrorCode = " + mErrorCode
                 + ", mAckPdu = " + mAckPdu
-                + ", mMessageId = " + mMessageId
-                        + "}";
+                + ", " + SmsController.formatCrossStackMessageId(mMessageId) + "}";
         return ret;
     }
 }
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index 7f07d4c..c1d4c3e 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.app.role.RoleManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -52,6 +53,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Pattern;
@@ -126,6 +128,8 @@
     /** Last modified time for pattern file */
     private long mPatternFileLastModified = 0;
 
+    private RoleManager mRoleManager;
+
     /** Directory for per-app SMS permission XML file. */
     private static final String SMS_POLICY_FILE_DIRECTORY = "/data/misc/sms";
 
@@ -249,6 +253,7 @@
     public SmsUsageMonitor(Context context) {
         mContext = context;
         ContentResolver resolver = context.getContentResolver();
+        mRoleManager = (RoleManager) mContext.getSystemService(Context.ROLE_SERVICE);
 
         mMaxAllowed = Settings.Global.getInt(resolver,
                 Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT,
@@ -346,7 +351,7 @@
     /**
      * Check to see if an application is allowed to send new SMS messages, and confirm with
      * user if the send limit was reached or if a non-system app is potentially sending to a
-     * premium SMS short code or number.
+     * premium SMS short code or number. If the app is the default SMS app, there's no send limit.
      *
      * @param appName the package name of the app requesting to send an SMS
      * @param smsWaiting the number of new messages desired to send
@@ -364,7 +369,12 @@
                 mSmsStamp.put(appName, sentList);
             }
 
-            return isUnderLimit(sentList, smsWaiting);
+            List<String> defaultApp = mRoleManager.getRoleHolders(RoleManager.ROLE_SMS);
+            if (defaultApp.contains(appName)) {
+                return true;
+            } else {
+                return isUnderLimit(sentList, smsWaiting);
+            }
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 7f4cfff..1044169 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -31,6 +31,7 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.database.ContentObserver;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -63,6 +64,7 @@
 import android.util.LocalLog;
 import android.util.Log;
 
+import com.android.ims.ImsManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IccCardConstants.State;
 import com.android.internal.telephony.dataconnection.DataEnabledOverride;
@@ -359,6 +361,29 @@
         invalidateActiveDataSubIdCaches();
         invalidateSlotIndexCaches();
 
+        mContext.getContentResolver().registerContentObserver(
+                SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI, false,
+                new ContentObserver(new Handler()) {
+                    @Override
+                    public void onChange(boolean selfChange, Uri uri) {
+                        if (uri.equals(SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI)) {
+                            refreshCachedActiveSubscriptionInfoList();
+                            notifySubscriptionInfoChanged();
+
+                            SubscriptionManager subManager = SubscriptionManager.from(mContext);
+                            for (SubscriptionInfo subInfo : getActiveSubscriptionInfoList(
+                                    mContext.getOpPackageName(), mContext.getAttributionTag())) {
+                                if (SubscriptionController.getInstance()
+                                        .isActiveSubId(subInfo.getSubscriptionId())) {
+                                    ImsManager imsManager = ImsManager.getInstance(mContext,
+                                            subInfo.getSimSlotIndex());
+                                    imsManager.updateImsServiceConfig();
+                                }
+                            }
+                        }
+                    }
+                });
+
         if (DBG) logdl("[SubscriptionController] init by Context");
     }
 
@@ -748,6 +773,7 @@
                     return subInfo;
                 }
             }
+
             // check cache for opportunistic subscriptions too, before querying db
             for (SubscriptionInfo subInfo : mCacheOpportunisticSubInfoList) {
                 if (subInfo.getSubscriptionId() == subId) {
@@ -2074,6 +2100,82 @@
         }
     }
 
+    /**
+     * Set device to device status sharing preference
+     * @param sharing the sharing preference to set
+     * @param subId
+     * @return the number of records updated
+     */
+    @Override
+    public int setDeviceToDeviceStatusSharing(int sharing, int subId) {
+        if (DBG) logd("[setDeviceToDeviceStatusSharing]- sharing:" + sharing + " subId:" + subId);
+
+        enforceModifyPhoneState("setDeviceToDeviceStatusSharing");
+
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            validateSubId(subId);
+            if (sharing < 0) {
+                if (DBG) logd("[setDeviceToDeviceStatusSharing]- fail");
+                return -1;
+            }
+            ContentValues value = new ContentValues(1);
+            value.put(SubscriptionManager.D2D_STATUS_SHARING, sharing);
+            if (DBG) logd("[setDeviceToDeviceStatusSharing]- sharing:" + sharing + " set");
+
+            int result = updateDatabase(value, subId, true);
+
+            // Refresh the Cache of Active Subscription Info List
+            refreshCachedActiveSubscriptionInfoList();
+
+            notifySubscriptionInfoChanged();
+
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Set contacts that allow device to device status sharing.
+     * @param contacts contacts to set
+     * @param subscriptionId
+     * @return the number of records updated
+     */
+    @Override
+    public int setDeviceToDeviceStatusSharingContacts(String contacts, int subscriptionId) {
+        if (DBG) {
+            logd("[setDeviceToDeviceStatusSharingContacts]- contacts:" + contacts
+                    + " subId:" + subscriptionId);
+        }
+
+        enforceModifyPhoneState("setDeviceToDeviceStatusSharingContacts");
+
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            validateSubId(subscriptionId);
+            ContentValues value = new ContentValues(1);
+            value.put(SubscriptionManager.D2D_STATUS_SHARING_SELECTED_CONTACTS, contacts);
+            if (DBG) {
+                logd("[setDeviceToDeviceStatusSharingContacts]- contacts:" + contacts
+                        + " set");
+            }
+
+            int result = updateDatabase(value, subscriptionId, true);
+
+            // Refresh the Cache of Active Subscription Info List
+            refreshCachedActiveSubscriptionInfoList();
+
+            notifySubscriptionInfoChanged();
+
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     public void syncGroupedSetting(int refSubId) {
         logd("syncGroupedSetting");
         try (Cursor cursor = mContext.getContentResolver().query(
@@ -3012,6 +3114,7 @@
             case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
             case SubscriptionManager.IMS_RCS_UCE_ENABLED:
             case SubscriptionManager.CROSS_SIM_CALLING_ENABLED:
+            case SubscriptionManager.VOIMS_OPT_IN_STATUS:
                 value.put(propKey, Integer.parseInt(propValue));
                 break;
             case SubscriptionManager.ALLOWED_NETWORK_TYPES:
@@ -3089,6 +3192,9 @@
                         case SubscriptionManager.GROUP_UUID:
                         case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES:
                         case SubscriptionManager.ALLOWED_NETWORK_TYPES:
+                        case SubscriptionManager.VOIMS_OPT_IN_STATUS:
+                        case SubscriptionManager.D2D_STATUS_SHARING:
+                        case SubscriptionManager.D2D_STATUS_SHARING_SELECTED_CONTACTS:
                             resultValue = cursor.getString(0);
                             break;
                         default:
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index a76f0f6..9132e5e 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -457,8 +457,10 @@
             // At this phase, the subscription list is accessible. Treating NOT_READY
             // as equivalent to ABSENT, once the rest of the system can handle it.
             sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
-            updateSubscriptionInfoByIccId(phoneId, false /* updateEmbeddedSubs */);
+        } else {
+            sIccId[phoneId] = null;
         }
+        updateSubscriptionInfoByIccId(phoneId, false /* updateEmbeddedSubs */);
 
         broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_NOT_READY,
                 null);
@@ -579,15 +581,26 @@
          *  2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED
          *  /ACTION_SIM_APPLICATION_STATE_CHANGED
          *  3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
-         *  4. ACTION_CARRIER_CONFIG_CHANGED
+         *  4. restore sim-specific settings
+         *  5. ACTION_CARRIER_CONFIG_CHANGED
          */
         broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
         broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
         broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_LOADED);
         updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        /* Sim-specific settings restore depends on knowing both the mccmnc and the carrierId of the
+        sim which is why it must be done after #updateSubscriptionCarrierId(). It is done before
+        carrier config update to avoid any race conditions with user settings that depend on
+        carrier config*/
+        restoreSimSpecificSettingsForPhone(phoneId);
         updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
     }
 
+    private void restoreSimSpecificSettingsForPhone(int phoneId) {
+        SubscriptionManager subManager = SubscriptionManager.from(sContext);
+        subManager.restoreSimSpecificSettingsForIccIdFromBackup(sIccId[phoneId]);
+    }
+
     private void updateCarrierServices(int phoneId, String simState) {
         CarrierConfigManager configManager =
                 (CarrierConfigManager) sContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -707,7 +720,7 @@
         mSubscriptionController.clearSubInfoRecord(phoneId);
 
         // If SIM is not absent, insert new record or update existing record.
-        if (!ICCID_STRING_FOR_NO_SIM.equals(sIccId[phoneId])) {
+        if (!ICCID_STRING_FOR_NO_SIM.equals(sIccId[phoneId]) && sIccId[phoneId] != null) {
             logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: "
                     + sIccId[phoneId] + ", phoneId:" + phoneId);
             mSubscriptionManager.addSubscriptionInfoRecord(sIccId[phoneId], phoneId);
@@ -1117,12 +1130,10 @@
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected void broadcastSimStateChanged(int phoneId, String state, String reason) {
+        // Note: This intent is way deprecated and is only being kept around because there's no
+        // graceful way to deprecate a sticky broadcast that has a lot of listeners.
+        // DO NOT add any new extras to this broadcast -- it is not protected by any permissions.
         Intent i = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
-        // TODO - we'd like this intent to have a single snapshot of all sim state,
-        // but until then this should not use REPLACE_PENDING or we may lose
-        // information
-        // i.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
-        //         | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
         i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
         i.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
         i.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, state);
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 39a36af..4632a60 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -35,6 +35,7 @@
 import com.android.internal.telephony.cdma.EriManager;
 import com.android.internal.telephony.dataconnection.DataEnabledSettings;
 import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
 import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
@@ -69,6 +70,7 @@
     private static final String TAG = TelephonyComponentFactory.class.getSimpleName();
 
     private static TelephonyComponentFactory sInstance;
+    private final TelephonyFacade mTelephonyFacade = new TelephonyFacade();
 
     private InjectedComponents mInjectedComponents;
 
@@ -447,4 +449,11 @@
             SubscriptionController sc) {
         return new SubscriptionInfoUpdater(looper, context, sc);
     }
+
+    /**
+     * Create a new LinkBandwidthEstimator.
+     */
+    public LinkBandwidthEstimator makeLinkBandwidthEstimator(Phone phone) {
+        return new LinkBandwidthEstimator(phone, mTelephonyFacade);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/TelephonyFacade.java b/src/java/com/android/internal/telephony/TelephonyFacade.java
new file mode 100644
index 0000000..31a2242
--- /dev/null
+++ b/src/java/com/android/internal/telephony/TelephonyFacade.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.net.TrafficStats;
+import android.os.SystemClock;
+
+/**
+ * This class is a wrapper of various static methods to simplify unit tests with static methods
+ */
+public class TelephonyFacade {
+    /**
+     * Returns milliseconds since boot, including time spent in sleep.
+     *
+     * @return Current time since boot in milliseconds.
+     */
+    public long getElapsedSinceBootMillis() {
+        return SystemClock.elapsedRealtime();
+    }
+
+    /**
+     * Wrapper for {@link TrafficStats#getMobileTxBytes}.
+     */
+    public long getMobileTxBytes() {
+        return TrafficStats.getMobileTxBytes();
+    }
+
+    /**
+     * Wrapper for {@link TrafficStats#getMobileRxBytes}.
+     */
+    public long getMobileRxBytes() {
+        return TrafficStats.getMobileRxBytes();
+    }
+}
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java
index 11e4709..d502aad 100755
--- a/src/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony;
 
+import static android.os.PowerWhitelistManager.REASON_EVENT_MMS;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+
 import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;
 
 import android.annotation.NonNull;
@@ -369,7 +372,8 @@
                 } else {
                     synchronized (this) {
                         mPowerWhitelistManager.whitelistAppTemporarilyForEvent(
-                                mWapPushManagerPackage, PowerWhitelistManager.EVENT_MMS, "mms-mgr");
+                                mWapPushManagerPackage, PowerWhitelistManager.EVENT_MMS,
+                                REASON_EVENT_MMS, "mms-mgr");
                     }
 
                     Intent intent = new Intent();
@@ -429,9 +433,13 @@
             if (DBG) Rlog.v(TAG, "Delivering MMS to: " + componentName.getPackageName() +
                     " " + componentName.getClassName());
             long duration = mPowerWhitelistManager.whitelistAppTemporarilyForEvent(
-                    componentName.getPackageName(), PowerWhitelistManager.EVENT_MMS, "mms-app");
+                    componentName.getPackageName(), PowerWhitelistManager.EVENT_MMS,
+                    REASON_EVENT_MMS, "mms-app");
             BroadcastOptions bopts = BroadcastOptions.makeBasic();
-            bopts.setTemporaryAppWhitelistDuration(duration);
+            bopts.setTemporaryAppAllowlist(duration,
+                    TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                    REASON_EVENT_MMS,
+                    "");
             options = bopts.toBundle();
         }
 
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index feec32a..1742db7 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -30,6 +30,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsController;
 import com.android.internal.telephony.SmsDispatchersController;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
@@ -109,7 +110,7 @@
                 + " mMessageRef=" + tracker.mMessageRef
                 + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
                 + " SS=" + ss
-                + " id=" + tracker.mMessageId);
+                + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
 
         // if sms over IMS is not supported on data and voice is not available...
         if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
index 3cdfffa..b7e009b 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
@@ -34,10 +34,13 @@
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.SparseArray;
 
+import com.android.internal.R;
 import com.android.internal.telephony.GsmCdmaPhone;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.cdnr.EfData.EFSource;
@@ -377,6 +380,48 @@
         return result;
     }
 
+    private CarrierDisplayNameData getCarrierDisplayNameFromCrossSimCallingOverride(
+            CarrierDisplayNameData rawCarrierDisplayNameData) {
+        PersistableBundle config = getCarrierConfig();
+        int crossSimSpnFormatIdx =
+                config.getInt(CarrierConfigManager.KEY_CROSS_SIM_SPN_FORMAT_INT);
+        boolean useRootLocale =
+                config.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE);
+
+        String[] crossSimSpnFormats = SubscriptionManager.getResourcesForSubId(
+                mPhone.getContext(),
+                mPhone.getSubId(), useRootLocale)
+                .getStringArray(R.array.crossSimSpnFormats);
+
+        if (crossSimSpnFormatIdx < 0 || crossSimSpnFormatIdx >= crossSimSpnFormats.length) {
+            Rlog.e(TAG, "updateSpnDisplay: KEY_CROSS_SIM_SPN_FORMAT_INT out of bounds: "
+                    + crossSimSpnFormatIdx);
+            crossSimSpnFormatIdx = 0;
+        }
+        String crossSimSpnFormat = crossSimSpnFormats[crossSimSpnFormatIdx];
+        // Override the spn, data spn, plmn by Cross-SIM Calling
+        List<PlmnNetworkName> efPnn = getEfPnn();
+        String plmn = efPnn.isEmpty() ? "" : getPlmnNetworkName(efPnn.get(0));
+        CarrierDisplayNameData result = rawCarrierDisplayNameData;
+        String rawSpn = rawCarrierDisplayNameData.getSpn();
+        String rawPlmn = TextUtils.isEmpty(plmn) ? rawCarrierDisplayNameData.getPlmn() : plmn;
+        String crossSimSpn = String.format(crossSimSpnFormat, rawSpn);
+        String crossSimPlmn = String.format(crossSimSpnFormat, plmn);
+        if (!TextUtils.isEmpty(rawSpn) && !TextUtils.isEmpty(crossSimSpn)) {
+            result = new CarrierDisplayNameData.Builder()
+                    .setSpn(crossSimSpn)
+                    .setDataSpn(crossSimSpn)
+                    .setShowSpn(true)
+                    .build();
+        } else if (!TextUtils.isEmpty(rawPlmn) && !TextUtils.isEmpty(crossSimPlmn)) {
+            result = new CarrierDisplayNameData.Builder()
+                    .setPlmn(crossSimPlmn)
+                    .setShowPlmn(true)
+                    .build();
+        }
+        return result;
+    }
+
     /**
      * Override the given carrier display name data {@code data} by out of service rule.
      * @param data the carrier display name data need to be overridden.
@@ -423,7 +468,13 @@
     private void resolveCarrierDisplayName() {
         CarrierDisplayNameData data = getCarrierDisplayNameFromEf();
         if (DBG) Rlog.d(TAG, "CarrierName from EF: " + data);
-        if (getCombinedRegState(getServiceState()) == ServiceState.STATE_IN_SERVICE) {
+        if ((mPhone.getImsPhone() != null) && (mPhone.getImsPhone().getImsRegistrationTech()
+                == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM)) {
+            data = getCarrierDisplayNameFromCrossSimCallingOverride(data);
+            if (DBG) {
+                Rlog.d(TAG, "CarrierName override by Cross-SIM Calling " + data);
+            }
+        } else if (getCombinedRegState(getServiceState()) == ServiceState.STATE_IN_SERVICE) {
             if (mPhone.isWifiCallingEnabled()) {
                 data = getCarrierDisplayNameFromWifiCallingOverride(data);
                 if (DBG) {
diff --git a/src/java/com/android/internal/telephony/d2d/Communicator.java b/src/java/com/android/internal/telephony/d2d/Communicator.java
index 8c3c630..c370811 100644
--- a/src/java/com/android/internal/telephony/d2d/Communicator.java
+++ b/src/java/com/android/internal/telephony/d2d/Communicator.java
@@ -24,7 +24,9 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Responsible for facilitating device-to-device communication between both ends of a call.
@@ -106,6 +108,9 @@
 
     public Communicator(@NonNull List<TransportProtocol> transportProtocols,
             @NonNull Callback callback) {
+        Log.i(this, "Initializing communicator with transports: %s",
+                transportProtocols.stream().map(p -> p.getClass().getSimpleName()).collect(
+                        Collectors.joining(",")));
         mTransportProtocols.addAll(transportProtocols);
         mTransportProtocols.forEach(p -> p.setCallback(this));
         mCallback = callback;
@@ -121,10 +126,11 @@
 
     /**
      * Handles state changes for a call.
-     * @param c The call in question.
+     * @param id The call in question.
      * @param state The new state.
      */
-    public void onStateChanged(Connection c, @Connection.ConnectionState int state) {
+    public void onStateChanged(String id, @Connection.ConnectionState int state) {
+        Log.i(this, "onStateChanged: id=%s, newState=%d", id, state);
         if (state == Connection.STATE_ACTIVE) {
             // Protocol negotiation can start as we are active
             if (mActiveTransport == null && !mIsNegotiationAttempted) {
@@ -291,4 +297,33 @@
         }
         return "";
     }
+
+    /**
+     * Test method used to force a transport type to be the active transport.
+     * @param transport The transport to activate.
+     */
+    public void setTransportActive(@NonNull String transport) {
+        Optional<TransportProtocol> tp = mTransportProtocols.stream()
+                .filter(t -> t.getClass().getSimpleName().equals(transport))
+                .findFirst();
+        if (!tp.isPresent()) {
+            Log.w(this, "setTransportActive: %s is not a valid transport.");
+            return;
+        }
+
+        mTransportProtocols.stream()
+                .filter(p -> p != tp.get())
+                .forEach(t -> t.forceNotNegotiated());
+        tp.get().forceNegotiated();
+        mActiveTransport = tp.get();
+        mIsNegotiated = true;
+        Log.i(this, "setTransportActive: %s has been forced active.", transport);
+    }
+
+    /**
+     * @return the list of {@link TransportProtocol} which are configured at the current time.
+     */
+    public @NonNull List<TransportProtocol> getTransportProtocols() {
+        return mTransportProtocols;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/d2d/DtmfTransport.java b/src/java/com/android/internal/telephony/d2d/DtmfTransport.java
index 5ec9e50..de26037 100644
--- a/src/java/com/android/internal/telephony/d2d/DtmfTransport.java
+++ b/src/java/com/android/internal/telephony/d2d/DtmfTransport.java
@@ -611,4 +611,14 @@
             }
         }
     }
+
+    @Override
+    public void forceNegotiated() {
+
+    }
+
+    @Override
+    public void forceNotNegotiated() {
+
+    }
 }
diff --git a/src/java/com/android/internal/telephony/d2d/MessageTypeAndValueHelper.java b/src/java/com/android/internal/telephony/d2d/MessageTypeAndValueHelper.java
new file mode 100644
index 0000000..2d36051
--- /dev/null
+++ b/src/java/com/android/internal/telephony/d2d/MessageTypeAndValueHelper.java
@@ -0,0 +1,97 @@
+/*
+ * 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.d2d;
+
+import android.telecom.Connection;
+import android.telecom.DiagnosticCall;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.BiMap;
+
+/**
+ * Helper class to map between the message types and values used in {@link Communicator} and those
+ * defined in the public API in {@link DiagnosticCall}.
+ */
+public class MessageTypeAndValueHelper {
+    // Maps between the local message and value types defined here and those defined in the
+    // DiagnosticCall class as part of the actual API.
+
+    /**
+     * Convert between the local message type (e.g.
+     * {@link Communicator#MESSAGE_CALL_RADIO_ACCESS_TYPE})
+     * and
+     * the ones referred to in {@link DiagnosticCall}.
+     */
+    public static final BiMap<Integer, Integer> MSG_TYPE_TO_DC_MSG_TYPE = new BiMap<>();
+
+    /**
+     * Convert between the local RAT type (e.g. {@link Communicator#RADIO_ACCESS_TYPE_IWLAN}) and
+     * the ones
+     * referred to by {@link DiagnosticCall#MESSAGE_CALL_NETWORK_TYPE}.
+     */
+    public static final BiMap<Integer, Integer> RAT_TYPE_TO_DC_NETWORK_TYPE = new BiMap<>();
+
+    /**
+     * Convert between the local codec (e.g. {@link Communicator#AUDIO_CODEC_AMR_WB}) and the ones
+     * referred to by {@link DiagnosticCall#MESSAGE_CALL_AUDIO_CODEC}.
+     */
+    public static final BiMap<Integer, Integer> CODEC_TO_DC_CODEC = new BiMap<>();
+
+    /**
+     * Convert between the local battery state (e.g. {@link Communicator#BATTERY_STATE_GOOD}) and
+     * the ones referred to by {@link DiagnosticCall#MESSAGE_DEVICE_BATTERY_STATE}.
+     */
+    public static final BiMap<Integer, Integer> BATTERY_STATE_TO_DC_BATTERY_STATE = new BiMap();
+
+    /**
+     * Convert between the local battery state (e.g. {@link Communicator#COVERAGE_GOOD}) and the
+     * ones referred to by {@link DiagnosticCall#MESSAGE_DEVICE_NETWORK_COVERAGE}.
+     */
+    public static final BiMap<Integer, Integer> COVERAGE_TO_DC_COVERAGE = new BiMap();
+
+    static {
+        MSG_TYPE_TO_DC_MSG_TYPE.put(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+                DiagnosticCall.MESSAGE_CALL_NETWORK_TYPE);
+        MSG_TYPE_TO_DC_MSG_TYPE.put(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+                DiagnosticCall.MESSAGE_CALL_AUDIO_CODEC);
+        MSG_TYPE_TO_DC_MSG_TYPE.put(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+                DiagnosticCall.MESSAGE_DEVICE_BATTERY_STATE);
+        MSG_TYPE_TO_DC_MSG_TYPE.put(Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
+                DiagnosticCall.MESSAGE_DEVICE_NETWORK_COVERAGE);
+
+        RAT_TYPE_TO_DC_NETWORK_TYPE.put(Communicator.RADIO_ACCESS_TYPE_LTE,
+                TelephonyManager.NETWORK_TYPE_LTE);
+        RAT_TYPE_TO_DC_NETWORK_TYPE.put(Communicator.RADIO_ACCESS_TYPE_IWLAN,
+                TelephonyManager.NETWORK_TYPE_IWLAN);
+        RAT_TYPE_TO_DC_NETWORK_TYPE.put(Communicator.RADIO_ACCESS_TYPE_NR,
+                TelephonyManager.NETWORK_TYPE_NR);
+
+        CODEC_TO_DC_CODEC.put(Communicator.AUDIO_CODEC_EVS, Connection.AUDIO_CODEC_EVS_WB);
+        CODEC_TO_DC_CODEC.put(Communicator.AUDIO_CODEC_AMR_WB, Connection.AUDIO_CODEC_AMR_WB);
+        CODEC_TO_DC_CODEC.put(Communicator.AUDIO_CODEC_AMR_NB, Connection.AUDIO_CODEC_AMR);
+
+        BATTERY_STATE_TO_DC_BATTERY_STATE.put(Communicator.BATTERY_STATE_LOW,
+                DiagnosticCall.BATTERY_STATE_LOW);
+        BATTERY_STATE_TO_DC_BATTERY_STATE.put(Communicator.BATTERY_STATE_GOOD,
+                DiagnosticCall.BATTERY_STATE_GOOD);
+        BATTERY_STATE_TO_DC_BATTERY_STATE.put(Communicator.BATTERY_STATE_CHARGING,
+                DiagnosticCall.BATTERY_STATE_CHARGING);
+
+        COVERAGE_TO_DC_COVERAGE.put(Communicator.COVERAGE_POOR, DiagnosticCall.COVERAGE_POOR);
+        COVERAGE_TO_DC_COVERAGE.put(Communicator.COVERAGE_GOOD, DiagnosticCall.COVERAGE_GOOD);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/d2d/RtpTransport.java b/src/java/com/android/internal/telephony/d2d/RtpTransport.java
index eb86aba..595a94c 100644
--- a/src/java/com/android/internal/telephony/d2d/RtpTransport.java
+++ b/src/java/com/android/internal/telephony/d2d/RtpTransport.java
@@ -20,6 +20,7 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.telecom.Log;
+import android.telephony.ims.ImsCallProfile;
 import android.telephony.ims.RtpHeaderExtension;
 import android.telephony.ims.RtpHeaderExtensionType;
 import android.util.ArraySet;
@@ -283,6 +284,15 @@
     private final Handler mHandler;
 
     /**
+     * {@code true} if the carrier supports negotiating the RTP header extensions using SDP.
+     * If {@code true}, we can expected the
+     * {@link ImsCallProfile#getAcceptedRtpHeaderExtensionTypes()} to contain the SDP negotiated RTP
+     * header extensions.  If {@code false} we will assume the protocol is negotiated only after
+     * receiving an RTP header extension of the expected type.
+     */
+    private final boolean mIsSdpNegotiationSupported;
+
+    /**
      * Protocol status.
      */
     private int mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_REQUIRED;
@@ -297,11 +307,14 @@
      * @param rtpAdapter Adapter for abstract send/receive of RTP header extension data.
      * @param timeoutsAdapter Timeouts adapter for dealing with time based configurations.
      * @param handler Handler for posting future events.
+     * @param isSdpNegotiationSupported Indicates whether SDP negotiation
      */
-    public RtpTransport(RtpAdapter rtpAdapter, Timeouts.Adapter timeoutsAdapter, Handler handler) {
+    public RtpTransport(RtpAdapter rtpAdapter, Timeouts.Adapter timeoutsAdapter, Handler handler,
+            boolean isSdpNegotiationSupported) {
         mRtpAdapter = rtpAdapter;
         mTimeoutsAdapter = timeoutsAdapter;
         mHandler = handler;
+        mIsSdpNegotiationSupported = isSdpNegotiationSupported;
     }
 
     /**
@@ -329,23 +342,36 @@
                 mRtpAdapter.getAcceptedRtpHeaderExtensions();
         mSupportedRtpHeaderExtensionTypes.addAll(acceptedExtensions);
 
-        boolean areExtensionsAvailable = acceptedExtensions.stream().anyMatch(
-                e -> e.getUri().equals(DEVICE_STATE_RTP_HEADER_EXTENSION))
-                && acceptedExtensions.stream().anyMatch(
+        Log.i(this, "startNegotiation: supportedExtensions=%s", mSupportedRtpHeaderExtensionTypes
+                .stream()
+                .map(e -> e.toString())
+                .collect(Collectors.joining(",")));
+
+        if (mIsSdpNegotiationSupported) {
+            boolean areExtensionsAvailable = acceptedExtensions.stream().anyMatch(
+                    e -> e.getUri().equals(DEVICE_STATE_RTP_HEADER_EXTENSION))
+                    && acceptedExtensions.stream().anyMatch(
                     e -> e.getUri().equals(CALL_STATE_RTP_HEADER_EXTENSION));
 
-        if (areExtensionsAvailable) {
-            // Headers were negotiated during SDP, so we can assume negotiation is complete and
-            // signal to the communicator that we can use this transport.
-            mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_COMPLETE;
-            Log.i(this, "startNegotiation: header extensions available, negotiation success");
-            notifyProtocolReady();
+            if (areExtensionsAvailable) {
+                // Headers were negotiated during SDP, so we can assume negotiation is complete and
+                // signal to the communicator that we can use this transport.
+                mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_COMPLETE;
+                Log.i(this, "startNegotiation: header extensions available, negotiation success");
+                notifyProtocolReady();
+            } else {
+                // Headers failed to be negotiated during SDP.   Assume protocol is not available.
+                // TODO: Implement fallback logic where we still try an SDP probe/response.
+                mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_FAILED;
+                Log.i(this,
+                        "startNegotiation: header extensions not available; negotiation failed");
+                notifyProtocolUnavailable();
+            }
         } else {
-            // Headers failed to be negotiated during SDP.   Assume protocol is not available.
-            // TODO: Implement fallback logic where we still try an SDP probe/response.
-            mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_FAILED;
-            Log.i(this, "startNegotiation: header extensions not available; negotiation failed");
-            notifyProtocolUnavailable();
+            Log.i(this, "startNegotiation: SDP negotiation not supported; negotiation complete");
+            // TODO: This is temporary; we will need to implement a probe/response in this scenario
+            // if SDP is not supported.  For now we will just assume the protocol is ready.
+            notifyProtocolReady();
         }
     }
 
@@ -358,10 +384,33 @@
     public void sendMessages(Set<Communicator.Message> messages) {
         Set<RtpHeaderExtension> toSend = messages.stream().map(m -> generateRtpHeaderExtension(m))
                 .collect(Collectors.toSet());
+        Log.i(this, "sendMessages: sending=%s", messages);
         mRtpAdapter.sendRtpHeaderExtensions(toSend);
     }
 
     /**
+     * Forces the protocol status to negotiated; for test purposes.
+     */
+    @Override
+    public void forceNegotiated() {
+        // If there is no supported RTP header extensions we need to fake it.
+        if (mSupportedRtpHeaderExtensionTypes == null
+                || mSupportedRtpHeaderExtensionTypes.isEmpty()) {
+            mSupportedRtpHeaderExtensionTypes.add(DEVICE_STATE_RTP_HEADER_EXTENSION_TYPE);
+            mSupportedRtpHeaderExtensionTypes.add(CALL_STATE_RTP_HEADER_EXTENSION_TYPE);
+        }
+        mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_COMPLETE;
+    }
+
+    /**
+     * Forces the protocol status to un-negotiated; for test purposes.
+     */
+    @Override
+    public void forceNotNegotiated() {
+        mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_REQUIRED;
+    }
+
+    /**
      * Called by the platform when RTP header extensions are received and need to be translated to
      * concrete messages.
      * Results in a callback via
diff --git a/src/java/com/android/internal/telephony/d2d/Timeouts.java b/src/java/com/android/internal/telephony/d2d/Timeouts.java
index 28ef0ec..e01c920 100644
--- a/src/java/com/android/internal/telephony/d2d/Timeouts.java
+++ b/src/java/com/android/internal/telephony/d2d/Timeouts.java
@@ -107,7 +107,7 @@
      * @return
      */
     public static long getDtmfNegotiationTimeoutMillis(ContentResolver cr) {
-        return get(cr, "dtmf_negotiation_timeout_millis", 1000L);
+        return get(cr, "dtmf_negotiation_timeout_millis", 3000L);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/d2d/TransportProtocol.java b/src/java/com/android/internal/telephony/d2d/TransportProtocol.java
index 4943788..91bf7d3 100644
--- a/src/java/com/android/internal/telephony/d2d/TransportProtocol.java
+++ b/src/java/com/android/internal/telephony/d2d/TransportProtocol.java
@@ -70,4 +70,14 @@
      * @param messages the messages to send via the transport.
      */
     void sendMessages(Set<Communicator.Message> messages);
+
+    /**
+     * Forces this transport to be in a negotiated state.
+     */
+    void forceNegotiated();
+
+    /**
+     * Forces this transport to be in a non-negotiated state.
+     */
+    void forceNotNegotiated();
 }
diff --git a/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java b/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java
index 9b1db18..b38e2be 100644
--- a/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java
+++ b/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java
@@ -29,7 +29,7 @@
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
 import android.telephony.data.DataServiceCallback;
-import android.telephony.data.SliceInfo;
+import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.TrafficDescriptor;
 
 import com.android.internal.telephony.CommandException;
@@ -160,7 +160,7 @@
         @Override
         public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
                 boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties,
-                int pduSessionId, SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+                int pduSessionId, NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
                 boolean matchAllRuleAllowed, DataServiceCallback callback) {
             if (DBG) log("setupDataCall " + getSlotIndex());
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index aaf71bd..c2ae9ce 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -69,9 +69,9 @@
 import android.telephony.data.DataProfile;
 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.SliceInfo;
 import android.telephony.data.TrafficDescriptor;
 import android.text.TextUtils;
 import android.util.LocalLog;
@@ -99,6 +99,7 @@
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -310,7 +311,7 @@
     private int mUplinkBandwidth = 14;
     private Qos mDefaultQos = null;
     private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
-    private SliceInfo mSliceInfo;
+    private NetworkSliceInfo mSliceInfo;
     private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
 
     /** The corresponding network agent for this data connection. */
@@ -376,7 +377,8 @@
     static final int EVENT_START_HANDOVER_ON_TARGET = BASE + 36;
     static final int EVENT_ALLOCATE_PDU_SESSION_ID = BASE + 37;
     static final int EVENT_RELEASE_PDU_SESSION_ID = BASE + 38;
-    private static final int CMD_TO_STRING_COUNT = EVENT_RELEASE_PDU_SESSION_ID - BASE + 1;
+    static final int EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE = BASE + 39;
+    private static final int CMD_TO_STRING_COUNT = EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE + 1;
 
     private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
     static {
@@ -426,6 +428,8 @@
         sCmdToString[EVENT_START_HANDOVER_ON_TARGET - BASE] = "EVENT_START_HANDOVER_ON_TARGET";
         sCmdToString[EVENT_ALLOCATE_PDU_SESSION_ID - BASE] = "EVENT_ALLOCATE_PDU_SESSION_ID";
         sCmdToString[EVENT_RELEASE_PDU_SESSION_ID - BASE] = "EVENT_RELEASE_PDU_SESSION_ID";
+        sCmdToString[EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE] =
+                "EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE";
     }
     // Convert cmd to string or null if unknown
     static String cmdToString(int cmd) {
@@ -483,6 +487,11 @@
         // Data can only be (temporarily) suspended while data is in active state
         if (getCurrentState() != mActiveState) return false;
 
+        // never set suspend for emergency apn
+        if (mApnSetting != null && mApnSetting.isEmergencyApn()) {
+            return false;
+        }
+
         // if we are not in-service change to SUSPENDED
         final ServiceStateTracker sst = mPhone.getServiceStateTracker();
         if (sst.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
@@ -617,7 +626,7 @@
         return mPduSessionId;
     }
 
-    public SliceInfo getSliceInfo() {
+    public NetworkSliceInfo getSliceInfo() {
         return mSliceInfo;
     }
 
@@ -881,8 +890,8 @@
         String dnn = null;
         String osAppId = null;
         if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
-            // TODO(b/181916712): update osAppId to use NetworkCapability API once it's available
-            osAppId = ApnSetting.getApnTypesStringFromBitmask(mApnSetting.getApnTypeBitmask());
+            osAppId = NetworkCapabilities.getCapabilityCarrierName(
+                    NetworkCapabilities.NET_CAPABILITY_ENTERPRISE);
         } else {
             dnn = mApnSetting.getApnName();
         }
@@ -948,6 +957,7 @@
             return DataFailCause.NONE;
         }
 
+        // setup data call for REQUEST_TYPE_NORMAL
         allocatePduSessionId(psi -> {
             this.setPduSessionId(psi);
             mDataServiceManager.setupDataCall(
@@ -974,7 +984,7 @@
             msg.obj = allocateCallback;
             mPhone.mCi.allocatePduSessionId(msg);
         } else {
-            allocateCallback.accept(EVENT_ALLOCATE_PDU_SESSION_ID);
+            allocateCallback.accept(PDU_SESSION_ID_NOT_SET);
         }
     }
 
@@ -1369,8 +1379,6 @@
         } else {
             if (DBG) log("onSetupConnectionCompleted received successful DataCallResponse");
             mCid = response.getId();
-            updateQosParameters(response);
-            updateSliceInfo(response);
 
             if (response.getPduSessionId() != getPduSessionId()) {
                 if (getDoAllocatePduSessionId()) {
@@ -1573,28 +1581,65 @@
                 uplinkUpdated = true;
             }
         }
+
         if (!downlinkUpdated || !uplinkUpdated) {
-            String ratName = ServiceState.rilRadioTechnologyToString(mRilRat);
-            if (mRilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && isNRConnected()) {
-                ratName = mPhone.getServiceState().getNrFrequencyRange()
-                        == ServiceState.FREQUENCY_RANGE_MMWAVE
-                        ? DctConstants.RAT_NAME_NR_NSA_MMWAVE : DctConstants.RAT_NAME_NR_NSA;
-            }
-            Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName);
-            if (values != null) {
-                if (!downlinkUpdated) {
-                    mDownlinkBandwidth = values.first;
-                }
-                if (!uplinkUpdated) {
-                    mUplinkBandwidth = values.second;
-                }
-            }
+            fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated);
+        }
+
+        if (mNetworkAgent != null) {
+            mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this);
+        }
+    }
+
+    private void updateLinkBandwidthsFromBandwidthEstimator(int uplinkBandwidthKbps,
+            int downlinkBandwidthKbps) {
+        if (DBG) {
+            log("updateLinkBandwidthsFromBandwidthEstimator, UL= "
+                    + uplinkBandwidthKbps + " DL= " + downlinkBandwidthKbps);
+        }
+        boolean downlinkUpdated = false;
+        boolean uplinkUpdated = false;
+        if (downlinkBandwidthKbps > 0) {
+            mDownlinkBandwidth = downlinkBandwidthKbps;
+            downlinkUpdated = true;
+        }
+        if (uplinkBandwidthKbps > 0) {
+            mUplinkBandwidth = uplinkBandwidthKbps;
+            uplinkUpdated = true;
+        }
+
+        if (!downlinkUpdated || !uplinkUpdated) {
+            fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated);
         }
         if (mNetworkAgent != null) {
             mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this);
         }
     }
 
+    private void fallBackToCarrierConfigValues(boolean downlinkUpdated, boolean uplinkUpdated) {
+        String ratName = ServiceState.rilRadioTechnologyToString(mRilRat);
+        if (mRilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && isNRConnected()) {
+            ratName = mPhone.getServiceState().getNrFrequencyRange()
+                    == ServiceState.FREQUENCY_RANGE_MMWAVE
+                    ? DctConstants.RAT_NAME_NR_NSA_MMWAVE : DctConstants.RAT_NAME_NR_NSA;
+        }
+        Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName);
+        if (values != null) {
+            if (!downlinkUpdated) {
+                mDownlinkBandwidth = values.first;
+            }
+            if (!uplinkUpdated) {
+                mUplinkBandwidth = values.second;
+            }
+        }
+    }
+
+    /** Update Tx and Rx link bandwidth estimation values */
+    public void updateLinkBandwidthEstimation(int uplinkBandwidthKbps, int downlinkBandwidthKbps) {
+        sendMessage(DataConnection.EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE,
+                new Pair<Integer, Integer>(uplinkBandwidthKbps, downlinkBandwidthKbps));
+    }
+
     private boolean isBandwidthSourceKey(String source) {
         return source.equals(mPhone.getContext().getResources().getString(
                 com.android.internal.R.string.config_bandwidthEstimateSource));
@@ -1722,11 +1767,11 @@
      * @return True if this data connection supports enterprise use.
      */
     private boolean isEnterpriseUse() {
-        // TODO(b/181916712): update osAppId to use NetworkCapability API once it's available
         boolean enterpriseTrafficDescriptor = mTrafficDescriptors
                 .stream()
                 .anyMatch(td -> td.getOsAppId() != null && td.getOsAppId().equals(
-                        ApnSetting.TYPE_ENTERPRISE_STRING));
+                        NetworkCapabilities.getCapabilityCarrierName(
+                                NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)));
         boolean enterpriseApnContext = mApnContexts.keySet()
                 .stream()
                 .anyMatch(ac -> ac.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE);
@@ -1832,8 +1877,7 @@
             builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         }
 
-        // TODO: Need to remove the use of hidden API deduceRestrictedCapability
-        if (builder.build().deduceRestrictedCapability()) {
+        if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())) {
             builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         }
 
@@ -3138,6 +3182,15 @@
                     retVal = HANDLED;
                     break;
                 }
+                case EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE: {
+                    Pair<Integer, Integer> pair = (Pair<Integer, Integer>) msg.obj;
+                    if (isBandwidthSourceKey(
+                            DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) {
+                        updateLinkBandwidthsFromBandwidthEstimator(pair.first, pair.second);
+                    }
+                    retVal = HANDLED;
+                    break;
+                }
                 case EVENT_REEVALUATE_RESTRICTED_STATE: {
                     // If the network was restricted, and now it does not need to be restricted
                     // anymore, we should add the NET_CAPABILITY_NOT_RESTRICTED capability.
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
index 5e37338..91a6d96 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
@@ -39,7 +39,7 @@
 import android.os.RegistrantList;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.permission.PermissionManager;
+import android.permission.LegacyPermissionManager;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.AnomalyReporter;
@@ -51,7 +51,7 @@
 import android.telephony.data.DataServiceCallback;
 import android.telephony.data.IDataService;
 import android.telephony.data.IDataServiceCallback;
-import android.telephony.data.SliceInfo;
+import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.TrafficDescriptor;
 import android.text.TextUtils;
 
@@ -91,7 +91,7 @@
 
     private final CarrierConfigManager mCarrierConfigManager;
     private final AppOpsManager mAppOps;
-    private final PermissionManager mPermissionManager;
+    private final LegacyPermissionManager mPermissionManager;
 
     private final int mTransportType;
 
@@ -228,6 +228,8 @@
                 mIDataService.createDataServiceProvider(mPhone.getPhoneId());
                 mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(),
                         new CellularDataServiceCallback("dataCallListChanged"));
+                mIDataService.registerForUnthrottleApn(mPhone.getPhoneId(),
+                        new CellularDataServiceCallback("unthrottleApn"));
             } catch (RemoteException e) {
                 mDeathRecipient.binderDied();
                 loge("Remote exception. " + e);
@@ -355,8 +357,8 @@
         // NOTE: Do NOT use AppGlobals to retrieve the permission manager; AppGlobals
         // caches the service instance, but we need to explicitly request a new service
         // so it can be mocked out for tests
-        mPermissionManager =
-                (PermissionManager) phone.getContext().getSystemService(Context.PERMISSION_SERVICE);
+        mPermissionManager = (LegacyPermissionManager) phone.getContext().getSystemService(
+                Context.LEGACY_PERMISSION_SERVICE);
         mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE);
 
         IntentFilter intentFilter = new IntentFilter();
@@ -500,7 +502,7 @@
      *
      * @return package name of the data service package for the the current transportType.
      */
-    private String getDataServicePackageName() {
+    public String getDataServicePackageName() {
         return getDataServicePackageName(mTransportType);
     }
 
@@ -632,7 +634,7 @@
      */
     public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
             boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            @Nullable  SliceInfo sliceInfo, @Nullable TrafficDescriptor trafficDescriptor,
+            @Nullable NetworkSliceInfo sliceInfo, @Nullable TrafficDescriptor trafficDescriptor,
             boolean matchAllRuleAllowed, Message onCompleteMessage) {
         if (DBG) log("setupDataCall");
         if (!mBound) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java b/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
index ebfe49d..532be97 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
@@ -127,6 +127,21 @@
         return RetryManager.NO_SUGGESTED_RETRY_DELAY;
     }
 
+    /**
+     * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}.
+     */
+    public void reset() {
+        final List<Integer> apnTypes = new ArrayList<>();
+        for (ThrottleStatus throttleStatus : mThrottleStatus.values()) {
+            apnTypes.add(throttleStatus.getApnType());
+        }
+
+        for (int apnType : apnTypes) {
+            setRetryTime(apnType, RetryManager.NO_SUGGESTED_RETRY_DELAY,
+                    DcTracker.REQUEST_TYPE_NORMAL);
+        }
+    }
+
     private ThrottleStatus createStatus(@Annotation.ApnType int apnType, long retryElapsedTime,
             @DcTracker.RequestNetworkType int newRequestType) {
         ThrottleStatus.Builder builder = new ThrottleStatus.Builder();
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index e40158e..16fdbb2 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -261,6 +261,9 @@
     private static final String INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE =
             "data_stall_alarm_extra_transport_type";
 
+    // Unique id for no data notification on setup data permanently failed.
+    private static final int NO_DATA_NOTIFICATION = 1001;
+
     /** The higher index has higher priority. */
     private static final DctConstants.State[] DATA_CONNECTION_STATE_PRIORITIES = {
             DctConstants.State.IDLE,
@@ -819,6 +822,8 @@
                 DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
         mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(mTransportType, this,
                 DctConstants.EVENT_DATA_RAT_CHANGED, null);
+        mPhone.getServiceStateTracker().registerForAirplaneModeChanged(this,
+                DctConstants.EVENT_AIRPLANE_MODE_CHANGED, null);
     }
 
     public void unregisterServiceStateTrackerEvents() {
@@ -829,6 +834,7 @@
         mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
         mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
         mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(mTransportType, this);
+        mPhone.getServiceStateTracker().unregisterForAirplaneModeChanged(this);
     }
 
     private void registerForAllEvents() {
@@ -853,6 +859,7 @@
         registerServiceStateTrackerEvents();
         mDataServiceManager.registerForServiceBindingChanged(this,
                 DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED, null);
+        mDataServiceManager.registerForApnUnthrottled(this, DctConstants.EVENT_APN_UNTHROTTLED);
     }
 
     public void dispose() {
@@ -910,6 +917,7 @@
         mDataServiceManager.unregisterForServiceBindingChanged(this);
         mDataEnabledSettings.unregisterForDataEnabledChanged(this);
         mDataEnabledSettings.unregisterForDataEnabledOverrideChanged(this);
+        mDataServiceManager.unregisterForApnUnthrottled(this);
     }
 
     /**
@@ -1935,6 +1943,9 @@
     }
 
     private int getPreferredApnSetId() {
+        // preferapnset uri returns all APNs for the current carrier which have an apn_set_id
+        // equal to the preferred APN (if no preferred APN, or if the preferred APN has no set id,
+        // the query will return null)
         Cursor c = mPhone.getContext().getContentResolver()
                 .query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI,
                     "preferapnset/subId/" + mPhone.getSubId()),
@@ -2302,9 +2313,13 @@
 
     protected void startReconnect(long delay, ApnContext apnContext,
             @RequestNetworkType int requestType) {
+        apnContext.setState(DctConstants.State.RETRYING);
         Message msg = obtainMessage(DctConstants.EVENT_DATA_RECONNECT,
                        mPhone.getSubId(), requestType, apnContext);
         cancelReconnect(apnContext);
+
+        // Wait a bit before trying the next APN, so that
+        // we're not tying up the RIL command channel
         sendMessageDelayed(msg, delay);
 
         if (DBG) {
@@ -2426,8 +2441,7 @@
         }
     }
 
-    private void onApnUnthrottled(Message msg) {
-        String apn = (String) msg.obj;
+    private void onApnUnthrottled(String apn) {
         if (apn != null) {
             ApnContext ac = mApnContexts.get(apn);
             if (ac != null) {
@@ -2505,6 +2519,19 @@
         }
     }
 
+    private void sendRequestNetworkCompleteMessages(@ApnType int apnType,
+            @RequestNetworkType int requestType, boolean success,
+            boolean fallbackOnFailedHandover) {
+        List<Message> messageList = mRequestNetworkCompletionMsgs.get(apnType);
+        if (messageList != null) {
+            for (Message msg : messageList) {
+                sendRequestNetworkCompleteMsg(msg, success, mTransportType, requestType,
+                        fallbackOnFailedHandover);
+            }
+            messageList.clear();
+        }
+    }
+
     private void sendRequestNetworkCompleteMsg(Message message, boolean success,
                                                @TransportType int transport,
                                                @RequestNetworkType int requestType,
@@ -2905,6 +2932,16 @@
 
         startNetStatPoll();
         startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
+
+        PersistableBundle b = getCarrierConfig();
+        if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT
+                && b.getBoolean(CarrierConfigManager
+                .KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL)) {
+            NotificationManager notificationManager = (NotificationManager)
+                    mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
+            notificationManager.cancel(Integer.toString(mPhone.getSubId()),
+                    NO_DATA_NOTIFICATION);
+        }
     }
 
     /**
@@ -2923,15 +2960,8 @@
                     + DataCallResponse.failureModeToString(handoverFailureMode));
         } else if (handoverFailureMode
                 != DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER) {
-            int apnType = ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType());
-            List<Message> messageList = mRequestNetworkCompletionMsgs.get(apnType);
-            if (messageList != null) {
-                for (Message msg : messageList) {
-                    sendRequestNetworkCompleteMsg(msg, success, mTransportType, requestType,
-                            fallbackOnFailedHandover);
-                }
-                messageList.clear();
-            }
+            sendRequestNetworkCompleteMessages(apnContext.getApnTypeBitmask(), requestType, success,
+                    fallbackOnFailedHandover);
         }
 
         if (success) {
@@ -3082,6 +3112,34 @@
                 log("cause=" + DataFailCause.toString(cause)
                         + ", mark apn as permanent failed. apn = " + apn);
                 apnContext.markApnPermanentFailed(apn);
+
+                PersistableBundle b = getCarrierConfig();
+                if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT
+                        && b.getBoolean(CarrierConfigManager
+                        .KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL)) {
+                    NotificationManager notificationManager = (NotificationManager)
+                            mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
+
+                    CharSequence title = mPhone.getContext().getText(
+                            com.android.internal.R.string.RestrictedOnDataTitle);
+                    CharSequence details = mPhone.getContext().getText(
+                            com.android.internal.R.string.RestrictedStateContent);
+
+                    Notification notification = new Notification.Builder(mPhone.getContext(),
+                            NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
+                            .setWhen(System.currentTimeMillis())
+                            .setAutoCancel(true)
+                            .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
+                            .setTicker(title)
+                            .setColor(mPhone.getContext().getResources().getColor(
+                                    com.android.internal.R.color.system_notification_accent_color))
+                            .setContentTitle(title)
+                            .setStyle(new Notification.BigTextStyle().bigText(details))
+                            .setContentText(details)
+                            .build();
+                    notificationManager.notify(Integer.toString(mPhone.getSubId()),
+                            NO_DATA_NOTIFICATION, notification);
+                }
             }
 
             int newRequestType = calculateNewRetryRequestType(handoverFailureMode, requestType,
@@ -3108,10 +3166,6 @@
                         + ". Request type=" + requestTypeToString(requestType) + ", Retry in "
                         + delay + "ms.");
             }
-            apnContext.setState(DctConstants.State.RETRYING);
-            // Wait a bit before trying the next APN, so that
-            // we're not tying up the RIL command channel
-
             startReconnect(delay, apnContext, requestType);
         } else {
             // If we are not going to retry any APN, set this APN context to failed state.
@@ -3120,6 +3174,9 @@
             apnContext.setDataConnection(null);
             log("onDataSetupCompleteError: Stop retrying APNs. delay=" + delay
                     + ", requestType=" + requestTypeToString(requestType));
+            //send request network complete messages as needed
+            sendRequestNetworkCompleteMessages(apnContext.getApnTypeBitmask(),
+                    requestType, false, fallbackOnFailedHandover);
         }
     }
 
@@ -3687,9 +3744,16 @@
                 break;
 
             case DctConstants.EVENT_TRY_SETUP_DATA:
-                trySetupData((ApnContext) msg.obj, msg.arg1);
+                apnContext = (ApnContext) msg.obj;
+                requestType = msg.arg1;
+                if (!trySetupData(apnContext, requestType)) {
+                    // Note that this might be a retry handover request that we need to notify
+                    // handover completion. Note if it fails, we will not retry anymore (because
+                    // it's due to pre-condition not met) and will not fallback.
+                    sendRequestNetworkCompleteMessages(apnContext.getApnTypeBitmask(), requestType,
+                            false, false);
+                }
                 break;
-
             case DctConstants.EVENT_CLEAN_UP_CONNECTION:
                 if (DBG) log("EVENT_CLEAN_UP_CONNECTION");
                 cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, (ApnContext) msg.obj);
@@ -3985,7 +4049,16 @@
                 onSimStateUpdated(simState);
                 break;
             case DctConstants.EVENT_APN_UNTHROTTLED:
-                onApnUnthrottled(msg);
+                ar = (AsyncResult) msg.obj;
+                String apn = (String) ar.result;
+                onApnUnthrottled(apn);
+                break;
+            case DctConstants.EVENT_AIRPLANE_MODE_CHANGED:
+                ar = (AsyncResult) msg.obj;
+                if (!(Boolean) ar.result) {
+                    log("Airplane Mode switched off, resetting data throttler");
+                    mDataThrottler.reset();
+                }
                 break;
             default:
                 Rlog.e("DcTracker", "Unhandled event=" + msg);
@@ -4262,7 +4335,7 @@
 
         if (isNetworkTypeUnmetered(NETWORK_TYPE_NR)) {
             if (mNrNsaMmwaveUnmetered) {
-                if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE) {
+                if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
                     if (DBG) log("NR unmetered for mmwave only");
                     return true;
                 }
@@ -4274,7 +4347,7 @@
                 }
                 return false;
             }
-            if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE
+            if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED
                     || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA
                     || rat == NETWORK_TYPE_NR) {
                 if (DBG) log("NR unmetered for all frequencies");
@@ -4285,7 +4358,7 @@
 
         if (mNrNsaAllUnmetered) {
             if (mNrNsaMmwaveUnmetered) {
-                if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE) {
+                if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
                     if (DBG) log("NR NSA unmetered for mmwave only via carrier configs");
                     return true;
                 }
@@ -4297,7 +4370,7 @@
                 }
                 return false;
             }
-            if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE
+            if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED
                     || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) {
                 if (DBG) log("NR NSA unmetered for all frequencies via carrier configs");
                 return true;
@@ -4361,7 +4434,7 @@
         boolean isNrNsa = (networkType == TelephonyManager.NETWORK_TYPE_LTE
                 || networkType == TelephonyManager.NETWORK_TYPE_LTE_CA)
                 && (overrideNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA
-                || overrideNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE);
+                || overrideNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED);
         boolean is5GHysteresisActive = mPhone.getDisplayInfoController().is5GHysteresisActive();
 
         // True if device is on NR SA or NR NSA, or neither but 5G hysteresis is active
@@ -4853,6 +4926,14 @@
         private long mTimeLastRecoveryStartMs;
         // Whether current network good or not
         private boolean mIsValidNetwork;
+        // Whether data stall happened or not.
+        private boolean mWasDataStall;
+        // Whether the result of last action(RADIO_RESTART) reported.
+        private boolean mLastActionReported;
+        // The real time for data stall start.
+        private long mDataStallStartMs;
+        // Last data stall action.
+        private @RecoveryAction int mLastAction;
 
         public DataStallRecoveryHandler() {
             reset();
@@ -4863,6 +4944,36 @@
             putRecoveryAction(RECOVERY_ACTION_GET_DATA_CALL_LIST);
         }
 
+        private void setNetworkValidationState(boolean isValid) {
+            // Validation status is true and was not data stall.
+            if (isValid && !mWasDataStall) {
+                return;
+            }
+
+            if (!mWasDataStall) {
+                mWasDataStall = true;
+                mDataStallStartMs = SystemClock.elapsedRealtime();
+                if (DBG) log("data stall: start time = " + mDataStallStartMs);
+                return;
+            }
+
+            if (!mLastActionReported) {
+                int timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
+                if (DBG) {
+                    log("data stall: lastaction = " + mLastAction + ", isRecovered = "
+                            + isValid + ", mTimeDuration = " + timeDuration);
+                }
+                DataStallRecoveryStats.onDataStallEvent(mLastAction, mPhone, isValid,
+                                                        timeDuration);
+                mLastActionReported = true;
+            }
+
+            if (isValid) {
+                mLastActionReported = false;
+                mWasDataStall = false;
+            }
+        }
+
         public boolean isAggressiveRecovery() {
             @RecoveryAction int action = getRecoveryAction();
 
@@ -4939,7 +5050,8 @@
                         mPhone.getPhoneId(), signalStrength);
                 TelephonyMetrics.getInstance().writeDataStallEvent(
                         mPhone.getPhoneId(), recoveryAction);
-                DataStallRecoveryStats.onDataStallEvent(recoveryAction, mPhone);
+                mLastAction = recoveryAction;
+                mLastActionReported = false;
                 broadcastDataStallDetected(recoveryAction);
 
                 switch (recoveryAction) {
@@ -4983,6 +5095,7 @@
         }
 
         public void processNetworkStatusChanged(boolean isValid) {
+            setNetworkValidationState(isValid);
             if (isValid) {
                 mIsValidNetwork = true;
                 reset();
@@ -5232,10 +5345,14 @@
 
         if (networkTypeBitmask == 0) {
             profileType = DataProfile.TYPE_COMMON;
-        } else if (ServiceState.bearerBitmapHasCdma(networkTypeBitmask)) {
+        } else if ((networkTypeBitmask & TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2)
+                == networkTypeBitmask) {
             profileType = DataProfile.TYPE_3GPP2;
-        } else {
+        } else if ((networkTypeBitmask & TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP)
+                == networkTypeBitmask) {
             profileType = DataProfile.TYPE_3GPP;
+        } else {
+            profileType = DataProfile.TYPE_COMMON;
         }
 
         return new DataProfile.Builder()
@@ -5269,6 +5386,9 @@
                     cleanUpAllConnectionsInternal(false, Phone.REASON_IWLAN_DATA_SERVICE_DIED);
                 }
             }
+        } else {
+            //reset throttling after binding to data service
+            mDataThrottler.reset();
         }
         mDataServiceBound = bound;
     }
diff --git a/src/java/com/android/internal/telephony/dataconnection/LinkBandwidthEstimator.java b/src/java/com/android/internal/telephony/dataconnection/LinkBandwidthEstimator.java
new file mode 100644
index 0000000..100b1b9
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/LinkBandwidthEstimator.java
@@ -0,0 +1,1102 @@
+/*
+ * 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.dataconnection;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.hardware.display.DisplayManager;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Message;
+import android.os.OutcomeReceiver;
+import android.preference.PreferenceManager;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityNr;
+import android.telephony.CellIdentityTdscdma;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.ModemActivityInfo;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.LocalLog;
+import android.util.Pair;
+import android.view.Display;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.DctConstants;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyFacade;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.telephony.Rlog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Link Bandwidth Estimator based on the byte counts in TrafficStats and the time reported in modem
+ * activity.
+ */
+public class LinkBandwidthEstimator extends Handler {
+    private static final String TAG = LinkBandwidthEstimator.class.getSimpleName();
+    private static final boolean DBG = false;
+    @VisibleForTesting
+    static final int MSG_SCREEN_STATE_CHANGED = 1;
+    @VisibleForTesting
+    static final int MSG_TRAFFIC_STATS_POLL = 2;
+    @VisibleForTesting
+    static final int MSG_MODEM_ACTIVITY_RETURNED = 3;
+    @VisibleForTesting
+    static final int MSG_DEFAULT_NETWORK_CHANGED = 4;
+    @VisibleForTesting
+    static final int MSG_SIGNAL_STRENGTH_CHANGED = 5;
+    @VisibleForTesting
+    static final int MSG_NR_FREQUENCY_CHANGED = 6;
+    @VisibleForTesting
+    static final int MSG_NR_STATE_CHANGED = 7;
+
+    // TODO: move the following parameters to xml file
+    private static final int TRAFFIC_STATS_POLL_INTERVAL_MS = 1_000;
+    private static final int MODEM_POLL_MIN_INTERVAL_MS = 5_000;
+    private static final int TRAFFIC_MODEM_POLL_BYTE_RATIO = 8;
+    private static final int TRAFFIC_POLL_BYTE_THRESHOLD_MAX = 20_000;
+    private static final int BYTE_DELTA_ACC_THRESHOLD_MAX_KB = 5_000;
+    private static final int MODEM_POLL_TIME_DELTA_MAX_MS = 15_000;
+    private static final int FILTER_UPDATE_MAX_INTERVAL_MS = 5_100;
+    // BW samples with Tx or Rx time below the following value is ignored.
+    private static final int TX_RX_TIME_MIN_MS = 200;
+    // The large time constant used in BW filter
+    private static final int TIME_CONSTANT_LARGE_SEC = 6;
+    // The small time constant used in BW filter
+    private static final int TIME_CONSTANT_SMALL_SEC = 6;
+    // If RSSI changes by more than the below value, update BW filter with small time constant
+    private static final int RSSI_DELTA_THRESHOLD_DB = 6;
+    // The up-scaling factor of filter coefficient.
+    private static final int FILTER_SCALE = 128;
+    // Force weight to 0 if the elapsed time is above LARGE_TIME_DECAY_RATIO * time constant
+    private static final int LARGE_TIME_DECAY_RATIO = 4;
+    // Modem Tx time may contain Rx time as defined in HAL. To work around the issue, if Tx time
+    // over Rx time ratio is above the following value, use Tx time + Rx time as Rx time.
+    private static final int TX_OVER_RX_TIME_RATIO_THRESHOLD_NUM = 3;
+    private static final int TX_OVER_RX_TIME_RATIO_THRESHOLD_DEN = 2;
+    // Default Link bandwidth value if the RAT entry is not found in static BW table.
+    private static final int DEFAULT_LINK_BAND_WIDTH_KBPS = 14;
+    // If Tx or Rx link bandwidth change is above the following value, send the BW update
+    private static final int BW_UPDATE_THRESHOLD_PERCENT = 15;
+
+    // To be used in link bandwidth estimation, each TrafficStats poll sample needs to be above
+    // a predefine threshold.
+    // For RAT with static BW above HIGH_BANDWIDTH_THRESHOLD_KBPS, it uses the following table.
+    // For others RATs, the thresholds are derived from the static BW values.
+    // The following table is defined per signal level, int [NUM_SIGNAL_LEVEL].
+    private static final int HIGH_BANDWIDTH_THRESHOLD_KBPS = 5000;
+    //Array dimension : int [NUM_LINK_DIRECTION][NUM_SIGNAL_LEVEL]
+    private static final int[][] BYTE_DELTA_THRESHOLD_KB =
+            {{200, 300, 400, 600, 1000}, {400, 600, 800, 1000, 1000}};
+    // Used to derive byte count threshold from avg BW
+    private static final int AVG_BW_TO_LOW_BW_RATIO = 4;
+    private static final int BYTE_DELTA_THRESHOLD_MIN_KB = 10;
+    private static final int MAX_ERROR_PERCENT = 100 * 100;
+    private static final String[] AVG_BW_PER_RAT = {
+            "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA:14,14",
+            "CDMA - 1xRTT:30,30", "CDMA - EvDo rev. 0:750,48", "CDMA - EvDo rev. A:950,550",
+            "HSDPA:4300,620", "HSUPA:4300,1800", "HSPA:4300,1800", "CDMA - EvDo rev. B:1500,550",
+            "CDMA - eHRPD:750,48", "HSPA+:13000,3400", "TD_SCDMA:115,115",
+            "LTE:30000,15000", "NR_NSA:47000,18000",
+            "NR_NSA_MMWAVE:145000,60000", "NR:145000,60000"};
+    private static final Map<String, Pair<Integer, Integer>> AVG_BW_PER_RAT_MAP = new ArrayMap<>();
+
+    // To be used in the long term avg, each count needs to be above the following value
+    static final int BW_STATS_COUNT_THRESHOLD = 5;
+    static final int NUM_SIGNAL_LEVEL = 5;
+    static final int LINK_TX = 0;
+    static final int LINK_RX = 1;
+    private static final int NUM_LINK_DIRECTION = 2;
+
+    private final Phone mPhone;
+    private final TelephonyFacade mTelephonyFacade;
+    private final TelephonyManager mTelephonyManager;
+    private final ConnectivityManager mConnectivityManager;
+    private final LocalLog mLocalLog = new LocalLog(512);
+    private boolean mScreenOn = false;
+    private boolean mIsOnDefaultRoute = false;
+    private long mLastModemPollTimeMs;
+    private boolean mLastTrafficValid = true;
+    private long mLastMobileTxBytes;
+    private long mLastMobileRxBytes;
+    private long mTxBytesDeltaAcc;
+    private long mRxBytesDeltaAcc;
+
+    private ModemActivityInfo mLastModemActivityInfo = null;
+    private final TelephonyCallback mTelephonyCallback = new TelephonyCallbackImpl();
+    private int mSignalStrengthDbm;
+    private int mSignalLevel;
+    private int mDataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    private int mTac;
+    private String mPlmn = "";
+    private NetworkCapabilities mNetworkCapabilities;
+    private NetworkBandwidth mPlaceholderNetwork;
+
+    private long mFilterUpdateTimeMs;
+
+    private int mBandwidthUpdateSignalDbm = -1;
+    private int mBandwidthUpdateSignalLevel = -1;
+    private int mBandwidthUpdateDataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    private String mBandwidthUpdatePlmn = "";
+    private BandwidthState mTxState = new BandwidthState(LINK_TX);
+    private BandwidthState mRxState = new BandwidthState(LINK_RX);
+
+    private static void initAvgBwPerRatTable() {
+        for (String config : AVG_BW_PER_RAT) {
+            int rxKbps = 14;
+            int txKbps = 14;
+            String[] kv = config.split(":");
+            if (kv.length == 2) {
+                String[] split = kv[1].split(",");
+                if (split.length == 2) {
+                    try {
+                        rxKbps = Integer.parseInt(split[0]);
+                        txKbps = Integer.parseInt(split[1]);
+                    } catch (NumberFormatException ignored) {
+                    }
+                }
+                AVG_BW_PER_RAT_MAP.put(kv[0], new Pair<>(rxKbps, txKbps));
+            }
+        }
+    }
+
+    private final DisplayManager.DisplayListener mDisplayListener =
+            new DisplayManager.DisplayListener() {
+                @Override
+                public void onDisplayAdded(int displayId) {
+                }
+
+                @Override
+                public void onDisplayRemoved(int displayId) {
+                }
+
+                @Override
+                public void onDisplayChanged(int displayId) {
+                    obtainMessage(MSG_SCREEN_STATE_CHANGED, isScreenOn()).sendToTarget();
+                }
+            };
+
+    private final OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+            mOutcomeReceiver =
+            new OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>() {
+                @Override
+                public void onResult(ModemActivityInfo result) {
+                    obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, result).sendToTarget();
+                }
+
+                @Override
+                public void onError(TelephonyManager.ModemActivityInfoException e) {
+                    Rlog.e(TAG, "error reading modem stats:" + e);
+                    obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, null).sendToTarget();
+                }
+            };
+
+    private final ConnectivityManager.NetworkCallback mDefaultNetworkCallback =
+            new ConnectivityManager.NetworkCallback() {
+                @Override
+                public void onCapabilitiesChanged(@NonNull Network network,
+                        @NonNull NetworkCapabilities networkCapabilities) {
+                    obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, networkCapabilities).sendToTarget();
+                }
+
+                public void onLost(@NonNull Network network) {
+                    obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, null).sendToTarget();
+                }
+            };
+
+    public LinkBandwidthEstimator(Phone phone, TelephonyFacade telephonyFacade) {
+        mPhone = phone;
+        mTelephonyFacade = telephonyFacade;
+        mTelephonyManager = phone.getContext()
+                .getSystemService(TelephonyManager.class)
+                .createForSubscriptionId(phone.getSubId());
+        mConnectivityManager = phone.getContext().getSystemService(ConnectivityManager.class);
+        DisplayManager dm = (DisplayManager) phone.getContext().getSystemService(
+                Context.DISPLAY_SERVICE);
+        dm.registerDisplayListener(mDisplayListener, null);
+        handleScreenStateChanged(isScreenOn());
+        mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
+        mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(this),
+                mTelephonyCallback);
+        mPlaceholderNetwork = new NetworkBandwidth("");
+        initAvgBwPerRatTable();
+        registerDataServiceState();
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        AsyncResult ar;
+        switch (msg.what) {
+            case MSG_SCREEN_STATE_CHANGED:
+                handleScreenStateChanged((boolean) msg.obj);
+                break;
+            case MSG_TRAFFIC_STATS_POLL:
+                handleTrafficStatsPoll();
+                break;
+            case MSG_MODEM_ACTIVITY_RETURNED:
+                handleModemActivityReturned((ModemActivityInfo) msg.obj);
+                break;
+            case MSG_DEFAULT_NETWORK_CHANGED:
+                handleDefaultNetworkChanged((NetworkCapabilities) msg.obj);
+                break;
+            case MSG_SIGNAL_STRENGTH_CHANGED:
+                handleSignalStrengthChanged((SignalStrength) msg.obj);
+                break;
+            case MSG_NR_FREQUENCY_CHANGED:
+                // fall through
+            case MSG_NR_STATE_CHANGED:
+                updateStaticBwValueResetFilter();
+                break;
+            default:
+                Rlog.e(TAG, "invalid message " + msg.what);
+                break;
+        }
+    }
+
+    /**
+     * @return True if one the device's screen (e.g. main screen, wifi display, HDMI display etc...)
+     * is on.
+     */
+    private boolean isScreenOn() {
+        // Note that we don't listen to Intent.SCREEN_ON and Intent.SCREEN_OFF because they are no
+        // longer adequate for monitoring the screen state since they are not sent in cases where
+        // the screen is turned off transiently such as due to the proximity sensor.
+        final DisplayManager dm = (DisplayManager) mPhone.getContext().getSystemService(
+                Context.DISPLAY_SERVICE);
+        Display[] displays = dm.getDisplays();
+
+        if (displays != null) {
+            for (Display display : displays) {
+                // Anything other than STATE_ON is treated as screen off, such as STATE_DOZE,
+                // STATE_DOZE_SUSPEND, etc...
+                if (display.getState() == Display.STATE_ON) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        return false;
+    }
+
+    private void handleScreenStateChanged(boolean screenOn) {
+        if (mScreenOn == screenOn) {
+            return;
+        }
+        mScreenOn = screenOn;
+        handleTrafficStatsPollConditionChanged();
+    }
+
+    private void handleDefaultNetworkChanged(NetworkCapabilities networkCapabilities) {
+        mNetworkCapabilities = networkCapabilities;
+        boolean isOnDefaultRoute;
+        if (networkCapabilities == null) {
+            isOnDefaultRoute = false;
+        } else {
+            isOnDefaultRoute = networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
+        }
+        if (mIsOnDefaultRoute == isOnDefaultRoute) {
+            return;
+        }
+        mIsOnDefaultRoute = isOnDefaultRoute;
+        handleTrafficStatsPollConditionChanged();
+    }
+
+    private void handleTrafficStatsPollConditionChanged() {
+        removeMessages(MSG_TRAFFIC_STATS_POLL);
+        if (mScreenOn && mIsOnDefaultRoute) {
+            updateDataRatCellIdentity();
+            handleTrafficStatsPoll();
+        }
+    }
+
+    private void handleTrafficStatsPoll() {
+        long mobileTxBytes = mTelephonyFacade.getMobileTxBytes();
+        long mobileRxBytes = mTelephonyFacade.getMobileRxBytes();
+        long txBytesDelta = mobileTxBytes - mLastMobileTxBytes;
+        long rxBytesDelta = mobileRxBytes - mLastMobileRxBytes;
+
+        // Schedule the next traffic stats poll
+        sendEmptyMessageDelayed(MSG_TRAFFIC_STATS_POLL, TRAFFIC_STATS_POLL_INTERVAL_MS);
+
+        mLastMobileTxBytes = mobileTxBytes;
+        mLastMobileRxBytes = mobileRxBytes;
+        // Sometimes TrafficStats byte counts return invalid values
+        // Ignore two polls if it happens
+        boolean trafficValid = txBytesDelta >= 0 && rxBytesDelta >= 0;
+        if (!mLastTrafficValid || !trafficValid) {
+            mLastTrafficValid = trafficValid;
+            Rlog.e(TAG, " run into invalid traffic count");
+            return;
+        }
+
+        mTxBytesDeltaAcc += txBytesDelta;
+        mRxBytesDeltaAcc += rxBytesDelta;
+
+        boolean doModemPoll = true;
+        // Check if it meets the requirement to request modem activity
+        long txByteDeltaThr = Math.min(mTxState.mByteDeltaAccThr / TRAFFIC_MODEM_POLL_BYTE_RATIO,
+                TRAFFIC_POLL_BYTE_THRESHOLD_MAX);
+        long rxByteDeltaThr = Math.min(mRxState.mByteDeltaAccThr / TRAFFIC_MODEM_POLL_BYTE_RATIO,
+                TRAFFIC_POLL_BYTE_THRESHOLD_MAX);
+        if (txBytesDelta < txByteDeltaThr && rxBytesDelta < rxByteDeltaThr
+                && mTxBytesDeltaAcc < mTxState.mByteDeltaAccThr
+                && mRxBytesDeltaAcc < mRxState.mByteDeltaAccThr) {
+            doModemPoll = false;
+        }
+
+        long currTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
+        long timeSinceLastModemPollMs = currTimeMs - mLastModemPollTimeMs;
+        if (timeSinceLastModemPollMs < MODEM_POLL_MIN_INTERVAL_MS) {
+            doModemPoll = false;
+        }
+
+        if (doModemPoll) {
+            StringBuilder sb = new StringBuilder();
+            logd(sb.append("txByteDelta ").append(txBytesDelta)
+                    .append(" rxByteDelta ").append(rxBytesDelta)
+                    .append(" txByteDeltaAcc ").append(mTxBytesDeltaAcc)
+                    .append(" rxByteDeltaAcc ").append(mRxBytesDeltaAcc)
+                    .append(" trigger modem activity request").toString());
+            updateDataRatCellIdentity();
+            // Filter update will happen after the request
+            makeRequestModemActivity();
+            return;
+        }
+
+        long timeSinceLastFilterUpdateMs = currTimeMs - mFilterUpdateTimeMs;
+        // Update filter
+        if (timeSinceLastFilterUpdateMs >= FILTER_UPDATE_MAX_INTERVAL_MS) {
+            updateDataRatCellIdentity();
+            updateTxRxBandwidthFilterSendToDataConnection();
+        }
+    }
+
+    private void makeRequestModemActivity() {
+        mLastModemPollTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
+        // TODO: add CountDown in case that onResult/OnError() never happen
+        mTelephonyManager.requestModemActivityInfo(Runnable::run, mOutcomeReceiver);
+    }
+
+    private void handleModemActivityReturned(ModemActivityInfo result) {
+        updateBandwidthTxRxSamples(result);
+        updateTxRxBandwidthFilterSendToDataConnection();
+        mLastModemActivityInfo = result;
+        // Update for next poll
+        resetByteDeltaAcc();
+        invalidateTxRxSamples();
+    }
+
+    private void resetByteDeltaAcc() {
+        mTxBytesDeltaAcc = 0;
+        mRxBytesDeltaAcc = 0;
+    }
+
+    private void invalidateTxRxSamples() {
+        mTxState.mBwSampleValid = false;
+        mRxState.mBwSampleValid = false;
+    }
+
+    private void updateBandwidthTxRxSamples(ModemActivityInfo modemActivityInfo) {
+        if (mLastModemActivityInfo == null || modemActivityInfo == null
+                || mNetworkCapabilities == null) {
+            return;
+        }
+
+        long lastTimeMs = mLastModemActivityInfo.getTimestampMillis();
+        long currTimeMs = modemActivityInfo.getTimestampMillis();
+        long timeDeltaMs = currTimeMs - lastTimeMs;
+
+        if (timeDeltaMs > MODEM_POLL_TIME_DELTA_MAX_MS || timeDeltaMs <= 0) {
+            return;
+        }
+        ModemActivityInfo deltaInfo = mLastModemActivityInfo.getDelta(modemActivityInfo);
+        long txTimeDeltaMs = getModemTxTimeMs(deltaInfo);
+        long rxTimeDeltaMs = deltaInfo.getReceiveTimeMillis();
+
+        // Check if txTimeDeltaMs / rxTimeDeltaMs > TX_OVER_RX_TIME_RATIO_THRESHOLD
+        boolean isTxTimeOverRxTimeRatioLarge = (txTimeDeltaMs * TX_OVER_RX_TIME_RATIO_THRESHOLD_DEN
+                > rxTimeDeltaMs * TX_OVER_RX_TIME_RATIO_THRESHOLD_NUM);
+        long rxTimeBwEstMs = isTxTimeOverRxTimeRatioLarge
+                ? (txTimeDeltaMs + rxTimeDeltaMs) : rxTimeDeltaMs;
+
+        mTxState.updateBandwidthSample(mTxBytesDeltaAcc, txTimeDeltaMs);
+        mRxState.updateBandwidthSample(mRxBytesDeltaAcc, rxTimeBwEstMs);
+
+        int reportedTxTputKbps = mNetworkCapabilities.getLinkUpstreamBandwidthKbps();
+        int reportedRxTputKbps = mNetworkCapabilities.getLinkDownstreamBandwidthKbps();
+
+        StringBuilder sb = new StringBuilder();
+        logd(sb.append("UpdateBwSample")
+                .append(" dBm ").append(mSignalStrengthDbm)
+                .append(" level ").append(mSignalLevel)
+                .append(" rat ").append(getDataRatName(mDataRat))
+                .append(" plmn ").append(mPlmn)
+                .append(" tac ").append(mTac)
+                .append(" reportedTxKbps ").append(reportedTxTputKbps)
+                .append(" reportedRxKbps ").append(reportedRxTputKbps)
+                .append(" txMs ").append(txTimeDeltaMs)
+                .append(" rxMs ").append(rxTimeDeltaMs)
+                .append(" txKB ").append(mTxBytesDeltaAcc / 1024)
+                .append(" rxKB ").append(mRxBytesDeltaAcc / 1024)
+                .append(" txKBThr ").append(mTxState.mByteDeltaAccThr / 1024)
+                .append(" rxKBThr ").append(mRxState.mByteDeltaAccThr / 1024)
+                .toString());
+    }
+
+    private long getModemTxTimeMs(ModemActivityInfo modemActivity) {
+        long txTimeMs = 0;
+        for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) {
+            txTimeMs += modemActivity.getTransmitDurationMillisAtPowerLevel(lvl);
+        }
+        return txTimeMs;
+    }
+
+    private void updateTxRxBandwidthFilterSendToDataConnection() {
+        mFilterUpdateTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
+        mTxState.updateBandwidthFilter();
+        mRxState.updateBandwidthFilter();
+
+        if (mTxState.hasLargeBwChange()
+                || mRxState.hasLargeBwChange()
+                || mBandwidthUpdateDataRat != mDataRat
+                || mBandwidthUpdateSignalLevel != mSignalLevel
+                || !mBandwidthUpdatePlmn.equals(mPlmn)) {
+            mTxState.mLastReportedBwKbps = mTxState.mAvgUsedKbps < 0 ? -1 : mTxState.mFilterKbps;
+            mRxState.mLastReportedBwKbps  = mRxState.mAvgUsedKbps < 0 ? -1 : mRxState.mFilterKbps;
+            sendLinkBandwidthToDataConnection(
+                    mTxState.mLastReportedBwKbps,
+                    mRxState.mLastReportedBwKbps);
+        }
+        mBandwidthUpdateSignalDbm = mSignalStrengthDbm;
+        mBandwidthUpdateSignalLevel = mSignalLevel;
+        mBandwidthUpdateDataRat = mDataRat;
+        mBandwidthUpdatePlmn = mPlmn;
+
+        mTxState.calculateError();
+        mRxState.calculateError();
+    }
+
+    private class BandwidthState {
+        private final int mLink;
+        int mFilterKbps;
+        int mByteDeltaAccThr = BYTE_DELTA_THRESHOLD_KB[0][0];
+        int mAvgUsedKbps;
+        int mBwSampleKbps;
+        boolean mBwSampleValid;
+        long mBwSampleValidTimeMs;
+        int mStaticBwKbps;
+        int mLastReportedBwKbps;
+        private final Map<String, EstimationError> mErrorMap = new ArrayMap<>();
+
+        private class EstimationError {
+            final String mRatName;
+            final long[] mBwEstIntNse = new long[NUM_SIGNAL_LEVEL];
+            final long[] mBwEstExtNse = new long[NUM_SIGNAL_LEVEL];
+            final long[] mStaticBwNse = new long[NUM_SIGNAL_LEVEL];
+            final int[] mCount = new int[NUM_SIGNAL_LEVEL];
+
+            EstimationError(String ratName) {
+                mRatName = ratName;
+            }
+
+            @Override
+            public String toString() {
+                StringBuilder sb = new StringBuilder();
+                return sb.append(mRatName)
+                        .append("\n Internal\n").append(printAvgError(mBwEstIntNse, mCount))
+                        .append("\n External\n").append(printAvgError(mBwEstExtNse, mCount))
+                        .append("\n StaticBw\n").append(printAvgError(mStaticBwNse, mCount))
+                        .toString();
+            }
+
+            private String printAvgError(long[] stats, int[] count) {
+                StringBuilder sb = new StringBuilder();
+                for (int k = 0; k < NUM_SIGNAL_LEVEL; k++) {
+                    int avgStat = (count[k] >= BW_STATS_COUNT_THRESHOLD && stats[k] >= 0)
+                            ? (int) Math.sqrt(stats[k] / count[k]) : 0;
+                    sb.append(" " + avgStat);
+                }
+                return sb.toString();
+            }
+        }
+
+        BandwidthState(int link) {
+            mLink = link;
+        }
+
+        private EstimationError lookupEstimationError(String dataRatName) {
+            EstimationError ans = mErrorMap.get(dataRatName);
+            if (ans == null) {
+                ans = new EstimationError(dataRatName);
+                mErrorMap.put(dataRatName, ans);
+            }
+            return ans;
+        }
+
+        private void updateBandwidthSample(long bytesDelta, long timeDeltaMs) {
+            if (bytesDelta < mByteDeltaAccThr) {
+                return;
+            }
+            if (timeDeltaMs < TX_RX_TIME_MIN_MS) {
+                return;
+            }
+            int linkBandwidthKbps = (int) (bytesDelta * 8 * 1000 / timeDeltaMs / 1024);
+            mBwSampleValid = true;
+            mBwSampleKbps = linkBandwidthKbps;
+
+            String dataRatName = getDataRatName(mDataRat);
+            NetworkBandwidth network = lookupNetwork(mPlmn, dataRatName);
+            // Update per RAT stats of all TAC
+            network.update(linkBandwidthKbps, mLink, mSignalLevel);
+
+            // Update per TAC stats
+            network = lookupNetwork(mPlmn, mTac, dataRatName);
+            network.update(linkBandwidthKbps, mLink, mSignalLevel);
+        }
+
+        private void updateBandwidthFilter() {
+            int avgKbps = getAvgLinkBandwidthKbps();
+            // Feed the filter with the long term avg if there is no valid BW sample so that filter
+            // will gradually converge the long term avg.
+            int filterInKbps = mBwSampleValid ? mBwSampleKbps : avgKbps;
+
+            long currTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
+            int timeDeltaSec = (int) (currTimeMs - mBwSampleValidTimeMs) / 1000;
+
+            // If the operation condition changes significantly since the last update
+            // or the sample has higher BW, use a faster filter. Otherwise, use a slow filter
+            int timeConstantSec;
+            if (Math.abs(mBandwidthUpdateSignalDbm - mSignalStrengthDbm) > RSSI_DELTA_THRESHOLD_DB
+                    || !mBandwidthUpdatePlmn.equals(mPlmn)
+                    || mBandwidthUpdateDataRat != mDataRat
+                    || (mBwSampleValid && mBwSampleKbps > avgKbps)) {
+                timeConstantSec = TIME_CONSTANT_SMALL_SEC;
+            } else {
+                timeConstantSec = TIME_CONSTANT_LARGE_SEC;
+            }
+            // Update timestamp for next iteration
+            if (mBwSampleValid) {
+                mBwSampleValidTimeMs = currTimeMs;
+            }
+
+            if (filterInKbps == mFilterKbps) {
+                logv(mLink + " skip filter because the same input / current = " + filterInKbps);
+                return;
+            }
+
+            int alpha = timeDeltaSec > LARGE_TIME_DECAY_RATIO * timeConstantSec ? 0
+                    : (int) (FILTER_SCALE * Math.exp(-1.0 * timeDeltaSec / timeConstantSec));
+            mFilterKbps = alpha == 0 ? filterInKbps : ((mFilterKbps * alpha
+                    + filterInKbps * FILTER_SCALE - filterInKbps * alpha) / FILTER_SCALE);
+            StringBuilder sb = new StringBuilder();
+            logv(sb.append(mLink)
+                    .append(" lastSampleWeight=").append(alpha)
+                    .append("/").append(FILTER_SCALE)
+                    .append(" filterInKbps=").append(filterInKbps)
+                    .append(" avgKbps=").append(avgKbps)
+                    .append(" filterOutKbps=").append(mFilterKbps)
+                    .toString());
+        }
+
+        private int getAvgUsedLinkBandwidthKbps() {
+            // Check if current TAC/RAT/level has enough stats
+            String dataRatName = getDataRatName(mDataRat);
+            NetworkBandwidth network = lookupNetwork(mPlmn, mTac, dataRatName);
+            int count = network.getCount(mLink, mSignalLevel);
+            if (count >= BW_STATS_COUNT_THRESHOLD) {
+                return (int) (network.getValue(mLink, mSignalLevel) / count);
+            }
+
+            // Check if current RAT/level has enough stats
+            network = lookupNetwork(mPlmn, dataRatName);
+            count = network.getCount(mLink, mSignalLevel);
+            if (count >= BW_STATS_COUNT_THRESHOLD) {
+                return (int) (network.getValue(mLink, mSignalLevel) / count);
+            }
+            return -1;
+        }
+
+        private int getCurrentCount() {
+            String dataRatName = getDataRatName(mDataRat);
+            NetworkBandwidth network = lookupNetwork(mPlmn, dataRatName);
+            return network.getCount(mLink, mSignalLevel);
+        }
+
+        /** get a long term avg value (PLMN/RAT/TAC/level dependent) or static value */
+        private int getAvgLinkBandwidthKbps() {
+            mAvgUsedKbps = getAvgUsedLinkBandwidthKbps();
+            if (mAvgUsedKbps > 0) {
+                return mAvgUsedKbps;
+            }
+            // Fall back to static value
+            return mStaticBwKbps;
+        }
+
+        private void resetBandwidthFilter() {
+            mFilterKbps = getAvgLinkBandwidthKbps();
+        }
+
+        private void updateByteCountThr() {
+            // For high BW RAT cases, use predefined value + threshold derived from avg usage BW
+            if (mStaticBwKbps > HIGH_BANDWIDTH_THRESHOLD_KBPS) {
+                int lowBytes = calculateByteCountThreshold(getAvgUsedLinkBandwidthKbps(),
+                        MODEM_POLL_MIN_INTERVAL_MS);
+                // Start with a predefined value
+                mByteDeltaAccThr = BYTE_DELTA_THRESHOLD_KB[mLink][mSignalLevel] * 1024;
+                if (lowBytes > 0) {
+                    // Raise the threshold if the avg usage BW is high
+                    mByteDeltaAccThr = Math.max(lowBytes, mByteDeltaAccThr);
+                    mByteDeltaAccThr = Math.min(mByteDeltaAccThr,
+                            BYTE_DELTA_ACC_THRESHOLD_MAX_KB * 1024);
+                }
+                return;
+            }
+            // For low BW RAT cases, derive the threshold from avg BW values
+            mByteDeltaAccThr = calculateByteCountThreshold(mStaticBwKbps,
+                    MODEM_POLL_MIN_INTERVAL_MS);
+
+            mByteDeltaAccThr = Math.max(mByteDeltaAccThr, BYTE_DELTA_THRESHOLD_MIN_KB * 1024);
+            // Low BW RAT threshold value should be no more than high BW one.
+            mByteDeltaAccThr = Math.min(mByteDeltaAccThr, BYTE_DELTA_THRESHOLD_KB[mLink][0] * 1024);
+        }
+
+        // Calculate a byte count threshold for the given avg BW and observation window size
+        private int calculateByteCountThreshold(int avgBwKbps, int durationMs) {
+            return avgBwKbps / 8 * durationMs / AVG_BW_TO_LOW_BW_RATIO;
+        }
+
+        public boolean hasLargeBwChange() {
+            int deltaKbps = Math.abs(mLastReportedBwKbps - mFilterKbps);
+            return mAvgUsedKbps > 0
+                    && deltaKbps * 100 > BW_UPDATE_THRESHOLD_PERCENT * mLastReportedBwKbps;
+        }
+
+        public void calculateError() {
+            if (!mBwSampleValid || getCurrentCount() <= BW_STATS_COUNT_THRESHOLD + 1) {
+                return;
+            }
+            int bwEstExtErrPercent = calculateErrorPercent(mLastReportedBwKbps, mBwSampleKbps);
+            int bwEstAvgErrPercent = calculateErrorPercent(mAvgUsedKbps, mBwSampleKbps);
+            int bwEstIntErrPercent = calculateErrorPercent(mFilterKbps, mBwSampleKbps);
+            int coldStartErrPercent = calculateErrorPercent(mStaticBwKbps, mBwSampleKbps);
+            EstimationError err = lookupEstimationError(getDataRatName(mDataRat));
+            err.mBwEstIntNse[mSignalLevel] += bwEstIntErrPercent * bwEstIntErrPercent;
+            err.mBwEstExtNse[mSignalLevel] += bwEstExtErrPercent * bwEstExtErrPercent;
+            err.mStaticBwNse[mSignalLevel] += coldStartErrPercent * coldStartErrPercent;
+            err.mCount[mSignalLevel]++;
+            StringBuilder sb = new StringBuilder();
+            logd(sb.append(mLink)
+                    .append(" sampKbps ").append(mBwSampleKbps)
+                    .append(" filtKbps ").append(mFilterKbps)
+                    .append(" reportKbps ").append(mLastReportedBwKbps)
+                    .append(" avgUsedKbps ").append(mAvgUsedKbps)
+                    .append(" csKbps ").append(mStaticBwKbps)
+                    .append(" intErrPercent ").append(bwEstIntErrPercent)
+                    .append(" avgErrPercent ").append(bwEstAvgErrPercent)
+                    .append(" extErrPercent ").append(bwEstExtErrPercent)
+                    .append(" csErrPercent ").append(coldStartErrPercent)
+                    .toString());
+        }
+
+        private int calculateErrorPercent(int inKbps, int bwSampleKbps) {
+            int errorKbps = inKbps - bwSampleKbps;
+            int errorPercent = bwSampleKbps > 0 ? (errorKbps * 100 / bwSampleKbps) : 0;
+            return Math.max(-MAX_ERROR_PERCENT, Math.min(errorPercent, MAX_ERROR_PERCENT));
+        }
+    }
+
+    /**
+     * Update the byte count threshold.
+     * It should be called whenever the RAT or signal level is changed.
+     * For the RAT with high BW (4G and beyond), use BYTE_DELTA_THRESHOLD_KB table.
+     * For other RATs, derive the threshold based on the static BW values.
+     */
+    private void updateByteCountThr() {
+        mTxState.updateByteCountThr();
+        mRxState.updateByteCountThr();
+    }
+
+    // Reset BW filter to a long term avg value (PLMN/RAT/TAC dependent) or static BW value.
+    // It should be called whenever PLMN/RAT or static BW value is changed;
+    private void resetBandwidthFilter() {
+        StringBuilder sb = new StringBuilder();
+        mTxState.resetBandwidthFilter();
+        mRxState.resetBandwidthFilter();
+    }
+
+    private void sendLinkBandwidthToDataConnection(int linkBandwidthTxKps, int linkBandwidthRxKps) {
+        DcTracker dt = mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (dt == null) {
+            return;
+        }
+        DataConnection dc = dt.getDataConnectionByApnType(PhoneConstants.APN_TYPE_DEFAULT);
+        if (dc == null) {
+            return;
+        }
+        logv("send to DC tx " + linkBandwidthTxKps + " rx " + linkBandwidthRxKps);
+        dc.updateLinkBandwidthEstimation(linkBandwidthTxKps, linkBandwidthRxKps);
+    }
+
+    private void handleSignalStrengthChanged(SignalStrength signalStrength) {
+        if (signalStrength == null) {
+            return;
+        }
+
+        updateDataRatCellIdentity();
+
+        mSignalStrengthDbm = signalStrength.getDbm();
+        mSignalLevel = signalStrength.getLevel();
+        if (Math.abs(mBandwidthUpdateSignalDbm - mSignalStrengthDbm) > RSSI_DELTA_THRESHOLD_DB) {
+            updateByteCountThr();
+            updateTxRxBandwidthFilterSendToDataConnection();
+        }
+    }
+
+    private void registerDataServiceState() {
+        mPhone.getServiceStateTracker().registerForNrStateChanged(this,
+                MSG_NR_STATE_CHANGED, null);
+        mPhone.getServiceStateTracker().registerForNrFrequencyChanged(this,
+                MSG_NR_FREQUENCY_CHANGED, null);
+    }
+
+    private String getDataRatName(int rat) {
+        if (rat == TelephonyManager.NETWORK_TYPE_LTE && isNRConnected()) {
+            return mPhone.getServiceState().getNrFrequencyRange()
+                    == ServiceState.FREQUENCY_RANGE_MMWAVE
+                    ? DctConstants.RAT_NAME_NR_NSA_MMWAVE : DctConstants.RAT_NAME_NR_NSA;
+        }
+        return TelephonyManager.getNetworkTypeName(rat);
+    }
+
+    // Update avg BW values.
+    // It should be called whenever the RAT could be changed.
+    // return true if avg value is changed;
+    private boolean updateStaticBwValue(int dataRat) {
+        Pair<Integer, Integer> values = getStaticAvgBw(dataRat);
+        if (values == null) {
+            mTxState.mStaticBwKbps = DEFAULT_LINK_BAND_WIDTH_KBPS;
+            mRxState.mStaticBwKbps = DEFAULT_LINK_BAND_WIDTH_KBPS;
+            return true;
+        }
+        if (mTxState.mStaticBwKbps != values.second
+                || mRxState.mStaticBwKbps != values.first) {
+            mTxState.mStaticBwKbps = values.second;
+            mRxState.mStaticBwKbps = values.first;
+            return true;
+        }
+        return false;
+    }
+
+    /** get per-RAT static bandwidth value */
+    public Pair<Integer, Integer> getStaticAvgBw(int dataRat) {
+        String dataRatName = getDataRatName(dataRat);
+        Pair<Integer, Integer> values = AVG_BW_PER_RAT_MAP.get(dataRatName);
+        if (values == null) {
+            Rlog.e(TAG, dataRatName + " is not found in Avg BW table");
+        }
+        return values;
+    }
+
+    private void updateStaticBwValueResetFilter() {
+        if (updateStaticBwValue(mDataRat)) {
+            updateByteCountThr();
+            resetBandwidthFilter();
+            updateTxRxBandwidthFilterSendToDataConnection();
+        }
+    }
+
+    /** Check if the device is connected to NR 5G Non-Standalone network. */
+    private boolean isNRConnected() {
+        return mPhone.getServiceState().getNrState()
+                == NetworkRegistrationInfo.NR_STATE_CONNECTED;
+    }
+
+    private void updateDataRatCellIdentity() {
+        boolean updatedPlmn = false;
+        CellIdentity cellIdentity = mPhone.getCurrentCellIdentity();
+        mTac = getTac(cellIdentity);
+        String plmn;
+        if (cellIdentity.getPlmn() != null) {
+            plmn = cellIdentity.getPlmn();
+        } else {
+            if (cellIdentity.getOperatorAlphaShort() != null) {
+                plmn = cellIdentity.getOperatorAlphaShort().toString();
+            } else {
+                plmn = "";
+            }
+        }
+        if (mPlmn == null || !plmn.equals(mPlmn)) {
+            updatedPlmn = true;
+            mPlmn = plmn;
+        }
+
+        boolean updatedRat = false;
+        NetworkRegistrationInfo nri = mPhone.getServiceState().getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (nri != null) {
+            int dataRat = nri.getAccessNetworkTechnology();
+            if (dataRat != mDataRat) {
+                updatedRat = true;
+                mDataRat = dataRat;
+                updateStaticBwValue(mDataRat);
+                updateByteCountThr();
+            }
+        }
+
+        if (updatedPlmn || updatedRat) {
+            resetBandwidthFilter();
+            updateTxRxBandwidthFilterSendToDataConnection();
+        }
+    }
+
+    private int getTac(@NonNull CellIdentity cellIdentity) {
+        if (cellIdentity instanceof CellIdentityLte) {
+            return ((CellIdentityLte) cellIdentity).getTac();
+        }
+        if (cellIdentity instanceof CellIdentityNr) {
+            return ((CellIdentityNr) cellIdentity).getTac();
+        }
+        if (cellIdentity instanceof CellIdentityWcdma) {
+            return ((CellIdentityWcdma) cellIdentity).getLac();
+        }
+        if (cellIdentity instanceof CellIdentityTdscdma) {
+            return ((CellIdentityTdscdma) cellIdentity).getLac();
+        }
+        if (cellIdentity instanceof CellIdentityGsm) {
+            return ((CellIdentityGsm) cellIdentity).getLac();
+        }
+        return 0;
+    }
+
+    private class TelephonyCallbackImpl extends TelephonyCallback implements
+            TelephonyCallback.SignalStrengthsListener {
+        @Override
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            obtainMessage(MSG_SIGNAL_STRENGTH_CHANGED, signalStrength).sendToTarget();
+        }
+    }
+
+    void logv(String msg) {
+        if (DBG) Rlog.v(TAG, msg);
+    }
+
+    void logd(String msg) {
+        if (DBG) Rlog.d(TAG, msg);
+        mLocalLog.log(msg);
+    }
+
+    @VisibleForTesting
+    static final int UNKNOWN_TAC = -1;
+    // Map with NetworkKey as the key and NetworkBandwidth as the value.
+    // NetworkKey is specified by the PLMN, data RAT and TAC of network.
+    // NetworkBandwidth represents the bandwidth related stats of each network.
+    private final Map<NetworkKey, NetworkBandwidth> mNetworkMap = new ArrayMap<>();
+    private static class NetworkKey {
+        private final String mPlmn;
+        private final String mDataRat;
+        private final int mTac;
+        NetworkKey(String plmn, int tac, String dataRat) {
+            mPlmn = plmn;
+            mTac = tac;
+            mDataRat = dataRat;
+        }
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (o == null || !(o instanceof NetworkKey) || hashCode() != o.hashCode()) {
+                return false;
+            }
+
+            if (this == o) {
+                return true;
+            }
+
+            NetworkKey that = (NetworkKey) o;
+            return mPlmn.equals(that.mPlmn)
+                    && mTac == that.mTac
+                    && mDataRat.equals(that.mDataRat);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPlmn, mDataRat, mTac);
+        }
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Plmn").append(mPlmn)
+                    .append("Rat").append(mDataRat)
+                    .append("Tac").append(mTac)
+                    .toString();
+            return sb.toString();
+        }
+    }
+
+    @NonNull
+    private NetworkBandwidth lookupNetwork(String plmn, String dataRat) {
+        return lookupNetwork(plmn, UNKNOWN_TAC, dataRat);
+    }
+
+    /** Look up NetworkBandwidth and create a new one if it doesn't exist */
+    @VisibleForTesting
+    @NonNull
+    public NetworkBandwidth lookupNetwork(String plmn, int tac, String dataRat) {
+        if (plmn == null || dataRat.equals(
+                TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_UNKNOWN))) {
+            return mPlaceholderNetwork;
+        }
+        NetworkKey key = new NetworkKey(plmn, tac, dataRat);
+        NetworkBandwidth ans = mNetworkMap.get(key);
+        if (ans == null) {
+            ans = new NetworkBandwidth(key.toString());
+            mNetworkMap.put(key, ans);
+        }
+        return ans;
+    }
+
+    /** A class holding link bandwidth related stats */
+    @VisibleForTesting
+    public class NetworkBandwidth {
+        private final String mKey;
+        NetworkBandwidth(String key) {
+            mKey = key;
+        }
+
+        /** Update link bandwidth stats */
+        public void update(long value, int link, int level) {
+            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+                    mPhone.getContext());
+            String valueKey = getValueKey(link, level);
+            String countKey = getCountKey(link, level);
+            SharedPreferences.Editor editor = sp.edit();
+            long currValue = sp.getLong(valueKey, 0);
+            int currCount = sp.getInt(countKey, 0);
+            editor.putLong(valueKey, currValue + value);
+            editor.putInt(countKey, currCount + 1);
+            editor.apply();
+        }
+
+        private String getValueKey(int link, int level) {
+            return getDataKey(link, level) + "Data";
+        }
+
+        private String getCountKey(int link, int level) {
+            return getDataKey(link, level) + "Count";
+        }
+
+        private String getDataKey(int link, int level) {
+            StringBuilder sb = new StringBuilder();
+            return sb.append(mKey)
+                    .append("Link").append(link)
+                    .append("Level").append(level)
+                    .toString();
+        }
+
+        /** Get the accumulated bandwidth value */
+        public long getValue(int link, int level) {
+            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+                    mPhone.getContext());
+            String valueKey = getValueKey(link, level);
+            return sp.getLong(valueKey, 0);
+        }
+
+        /** Get the accumulated bandwidth count */
+        public int getCount(int link, int level) {
+            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+                    mPhone.getContext());
+            String countKey = getCountKey(link, level);
+            return sp.getInt(countKey, 0);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(mKey);
+            sb.append("\n");
+            for (int link = 0; link < NUM_LINK_DIRECTION; link++) {
+                sb.append((link == 0 ? "tx" : "rx"));
+                sb.append("\n avgKbps");
+                for (int level = 0; level < NUM_SIGNAL_LEVEL; level++) {
+                    int count = getCount(link, level);
+                    int avgKbps = count == 0 ? 0 : (int) (getValue(link, level) / count);
+                    sb.append(" ").append(avgKbps);
+                }
+                sb.append("\n count");
+                for (int level = 0; level < NUM_SIGNAL_LEVEL; level++) {
+                    int count = getCount(link, level);
+                    sb.append(" ").append(count);
+                }
+                sb.append("\n");
+            }
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Dump the internal state and local logs
+     */
+    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+        pw.increaseIndent();
+        pw.println("current PLMN " + mPlmn + " TAC " + mTac + " RAT " + getDataRatName(mDataRat));
+        pw.println("all networks visited since device boot");
+        for (NetworkBandwidth network : mNetworkMap.values()) {
+            pw.println(network.toString());
+        }
+
+        pw.println("Tx NRMSE");
+        for (BandwidthState.EstimationError err : mTxState.mErrorMap.values()) {
+            pw.println(err.toString());
+        }
+
+        pw.println("Rx NRMSE");
+        for (BandwidthState.EstimationError err : mRxState.mErrorMap.values()) {
+            pw.println(err.toString());
+        }
+
+        try {
+            mLocalLog.dump(fd, pw, args);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        pw.decreaseIndent();
+        pw.println();
+        pw.flush();
+    }
+
+}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 2d43271..2d81299 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -1293,7 +1293,10 @@
                 confirmationCodeRetried);
         intent.putExtra(EXTRA_OPERATION, op);
         PendingIntent resolutionIntent = PendingIntent.getActivity(
-                mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_ONE_SHOT);
+                mContext,
+                0 /* requestCode */,
+                intent,
+                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
         extrasIntent.putExtra(
                 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT, resolutionIntent);
     }
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index b113b5a..b3a8038 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -29,6 +29,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsController;
 import com.android.internal.telephony.SmsDispatchersController;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
@@ -156,7 +157,7 @@
                 + " mMessageRef=" + tracker.mMessageRef
                 + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
                 + " SS=" + ss
-                + " id=" + tracker.mMessageId);
+                + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
 
         // if sms over IMS is not supported on data and voice is not available...
         if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index 5e30a7a..b717582 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -1522,7 +1522,7 @@
         for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
                 serviceIntent,
                 PackageManager.GET_META_DATA,
-                UserHandle.getUserHandleForUid(UserHandle.myUserId()))) {
+                UserHandle.of(UserHandle.myUserId()))) {
             ServiceInfo serviceInfo = entry.serviceInfo;
 
             if (serviceInfo != null) {
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index 82122db..c47d5b0 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -28,7 +28,7 @@
 import android.os.IInterface;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.permission.PermissionManager;
+import android.permission.LegacyPermissionManager;
 import android.telephony.AnomalyReporter;
 import android.telephony.ims.ImsService;
 import android.telephony.ims.aidl.IImsConfig;
@@ -226,7 +226,7 @@
     private static final boolean ENFORCE_SINGLE_SERVICE_FOR_SIP_TRANSPORT = false;
     private final ComponentName mComponentName;
     private final HandlerThread mHandlerThread = new HandlerThread("ImsServiceControllerHandler");
-    private final PermissionManager mPermissionManager;
+    private final LegacyPermissionManager mPermissionManager;
     private ImsFeatureBinderRepository mRepo;
     private ImsServiceControllerCallbacks mCallbacks;
     private ExponentialBackoff mBackoff;
@@ -324,8 +324,8 @@
                 2, /* multiplier */
                 mHandlerThread.getLooper(),
                 mRestartImsServiceRunnable);
-        mPermissionManager =
-                (PermissionManager) mContext.getSystemService(Context.PERMISSION_SERVICE);
+        mPermissionManager = (LegacyPermissionManager) mContext.getSystemService(
+                Context.LEGACY_PERMISSION_SERVICE);
         mRepo = repo;
 
         mPackageManager = mContext.getPackageManager();
diff --git a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
index 7b0619b..b343763 100644
--- a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
+++ b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
@@ -65,6 +65,8 @@
                 TelephonyManager.NETWORK_TYPE_LTE);
         REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
                 TelephonyManager.NETWORK_TYPE_IWLAN);
+        REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM,
+                TelephonyManager.NETWORK_TYPE_IWLAN);
     }
 
     // Feature Type for compatibility with old "feature" updates
@@ -516,7 +518,8 @@
         Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
         intent.setPackage(TelephonyManager.PHONE_PROCESS_NAME);
         return PendingIntent.getBroadcast(mContext, 0, intent,
-                PendingIntent.FLAG_UPDATE_CURRENT);
+                // Mutable because information associated with the call is passed back here.
+                PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     private int convertCapability(int capability, int radioTech) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index bd68f66..d7aad9f 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -43,6 +43,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.os.Registrant;
 import android.os.RegistrantList;
@@ -61,6 +62,7 @@
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyLocalConnection;
 import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsCallProfile;
@@ -533,6 +535,8 @@
     private boolean mAlwaysPlayRemoteHoldTone = false;
     private boolean mAutoRetryFailedWifiEmergencyCall = false;
     private boolean mSupportCepOnPeer = true;
+    private boolean mSupportD2DUsingRtp = false;
+    private boolean mSupportSdpForRtpHeaderExtensions = false;
     // Tracks the state of our background/foreground calls while a call hold/swap operation is
     // in progress. Values listed above.
     private HoldSwapState mHoldSwitchingState = HoldSwapState.INACTIVE;
@@ -1026,16 +1030,7 @@
                     null);
         }
 
-        // Where device to device communication is available, ensure that the
-        // supported RTP header extension types defined in {@link RtpTransport} are
-        // set as the offered RTP header extensions for this device.
-        if (mConfig != null && mConfig.isD2DCommunicationSupported) {
-            ArraySet<RtpHeaderExtensionType> types = new ArraySet<>();
-            types.add(RtpTransport.CALL_STATE_RTP_HEADER_EXTENSION_TYPE);
-            types.add(RtpTransport.DEVICE_STATE_RTP_HEADER_EXTENSION_TYPE);
-            logi("connectionReady: set offered RTP header extension types");
-            mImsManager.setOfferedRtpHeaderExtensionTypes(types);
-        }
+        maybeConfigureRtpHeaderExtensions();
 
         if (mCarrierConfigLoaded) {
             mImsManager.updateImsServiceConfig();
@@ -1044,6 +1039,32 @@
         sendImsServiceStateIntent(ImsManager.ACTION_IMS_SERVICE_UP);
     }
 
+    /**
+     * Configures RTP header extension types used during SDP negotiation.
+     */
+    private void maybeConfigureRtpHeaderExtensions() {
+        // Where device to device communication is available, ensure that the
+        // supported RTP header extension types defined in {@link RtpTransport} are
+        // set as the offered RTP header extensions for this device.
+        if (mConfig != null && mConfig.isD2DCommunicationSupported && mSupportD2DUsingRtp) {
+            ArraySet<RtpHeaderExtensionType> types = new ArraySet<>();
+            if (mSupportSdpForRtpHeaderExtensions) {
+                types.add(RtpTransport.CALL_STATE_RTP_HEADER_EXTENSION_TYPE);
+                types.add(RtpTransport.DEVICE_STATE_RTP_HEADER_EXTENSION_TYPE);
+                logi("maybeConfigureRtpHeaderExtensions: set offered RTP header extension types");
+
+            } else {
+                logi("maybeConfigureRtpHeaderExtensions: SDP negotiation not supported; not "
+                        + "setting offered RTP header extension types");
+            }
+            try {
+                mImsManager.setOfferedRtpHeaderExtensionTypes(types);
+            } catch (ImsException e) {
+                loge("maybeConfigureRtpHeaderExtensions: failed to set extensions; " + e);
+            }
+        }
+    }
+
     private void stopListeningForCalls() {
         log("stopListeningForCalls");
         mOperationLocalLog.log("stopListeningForCalls - Disconnecting from ImsService");
@@ -1437,6 +1458,8 @@
         if (mImsManager != null) {
             mImsManager.updateImsServiceConfig();
         }
+        // Check for changes due to carrier config.
+        maybeConfigureRtpHeaderExtensions();
     }
 
     /**
@@ -1480,6 +1503,11 @@
                 CarrierConfigManager.KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL);
         mSupportCepOnPeer = carrierConfig.getBoolean(
                 CarrierConfigManager.KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL);
+        mSupportD2DUsingRtp = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL);
+        mSupportSdpForRtpHeaderExtensions = carrierConfig.getBoolean(
+                CarrierConfigManager
+                        .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL);
 
         if (mPhone.getContext().getResources().getBoolean(
                 com.android.internal.R.bool.config_allow_ussd_over_ims)) {
@@ -1565,6 +1593,9 @@
             ImsCallProfile profile = mImsManager.createCallProfile(serviceType, callType);
             if (conn.isAdhocConference()) {
                 profile.setCallExtraBoolean(ImsCallProfile.EXTRA_CONFERENCE, true);
+                // Also set value for EXTRA_CONFERENCE_DEPRECATED in case receivers are using old
+                // values.
+                profile.setCallExtraBoolean(ImsCallProfile.EXTRA_CONFERENCE_DEPRECATED, true);
             }
             profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode);
             profile.setCallExtraInt(ImsCallProfile.EXTRA_RETRY_CALL_FAIL_REASON,
@@ -1600,7 +1631,10 @@
 
                 if (intentExtras.containsKey(
                         android.telecom.TelecomManager.EXTRA_OUTGOING_PICTURE)) {
-                    // TODO(hallliu) Set ImsCallProfile.EXTRA_PICTURE_URL with cached URL string
+                    String url = TelephonyLocalConnection.getCallComposerServerUrlForHandle(
+                            mPhone.getSubId(), ((ParcelUuid) intentExtras.getParcelable(
+                                    TelecomManager.EXTRA_OUTGOING_PICTURE)).getUuid());
+                    profile.setCallExtra(ImsCallProfile.EXTRA_PICTURE_URL, url);
                 }
 
                 if (conn.hasRttTextStream()) {
@@ -2691,6 +2725,7 @@
                 return DisconnectCause.TIMED_OUT;
 
             case ImsReasonInfo.CODE_LOCAL_POWER_OFF:
+            case ImsReasonInfo.CODE_RADIO_OFF:
                 return DisconnectCause.POWER_OFF;
 
             case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
@@ -3081,6 +3116,7 @@
 
             if (conn != null) {
                 conn.setPreciseDisconnectCause(getPreciseDisconnectCauseFromReasonInfo(reasonInfo));
+                conn.setImsReasonInfo(reasonInfo);
             }
 
             if (reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
@@ -3761,6 +3797,15 @@
             }
             cqm.saveCallQuality(callQuality);
             mCallQualityMetrics.put(callId, cqm);
+
+            ImsPhoneConnection conn = findConnection(imsCall);
+            if (conn != null) {
+                Bundle report = new Bundle();
+                report.putParcelable(android.telecom.Connection.EXTRA_CALL_QUALITY_REPORT,
+                        callQuality);
+                conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_QUALITY_REPORT,
+                        report);
+            }
         }
 
         /**
@@ -4394,6 +4439,11 @@
         pw.println(" mSupportCepOnPeer=" + mSupportCepOnPeer);
         if (mConfig != null) {
             pw.println(" isDeviceToDeviceCommsSupported= " + mConfig.isD2DCommunicationSupported);
+            if (mConfig.isD2DCommunicationSupported) {
+                pw.println(" mSupportD2DUsingRtp= " + mSupportD2DUsingRtp);
+                pw.println(" mSupportSdpForRtpHeaderExtensions= "
+                        + mSupportSdpForRtpHeaderExtensions);
+            }
         }
         pw.println(" Event Log:");
         pw.increaseIndent();
@@ -4457,7 +4507,10 @@
 
     public boolean isVowifiEnabled() {
         return isImsCapabilityInCacheAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
+                || isImsCapabilityInCacheAvailable(
+                        MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                        ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM);
     }
 
     public boolean isVideoCallEnabled() {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index f2f8738..1371c92 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -26,7 +26,7 @@
 import android.telephony.SignalThresholdInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataProfile;
-import android.telephony.data.SliceInfo;
+import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.TrafficDescriptor;
 import android.telephony.emergency.EmergencyNumber;
 
@@ -286,8 +286,8 @@
     @Override
     public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
             boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
-            Message result) {
+            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+            boolean matchAllRuleAllowed, Message result) {
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index e9f17ab..8403fc9 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.imsphone;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.net.Uri;
@@ -39,6 +40,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.ims.AudioCodecAttributes;
 import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsStreamMediaProfile;
 import android.telephony.ims.RtpHeaderExtension;
 import android.telephony.ims.RtpHeaderExtensionType;
@@ -141,6 +143,12 @@
      */
     private boolean mIsLocalVideoCapable = true;
 
+    /**
+     * When the call is in a disconnected, state, will be set to the {@link ImsReasonInfo}
+     * associated with the disconnection, if known.
+     */
+    private ImsReasonInfo mImsReasonInfo;
+
     //***** Event Constants
     private static final int EVENT_DTMF_DONE = 1;
     private static final int EVENT_PAUSE_DONE = 2;
@@ -1420,6 +1428,14 @@
     }
 
     /**
+     * Indicates whether current phone connection is cross sim calling or not
+     * @return boolean: true if cross sim calling, false otherwise
+     */
+    public boolean isCrossSimCall() {
+        return mImsCall != null && mImsCall.isCrossSimCall();
+    }
+
+    /**
      * Handles notifications from the {@link ImsVideoCallProviderWrapper} of session modification
      * responses received.
      *
@@ -1568,6 +1584,23 @@
     }
 
     /**
+     * For a connection being disconnected, sets the {@link ImsReasonInfo} which describes the
+     * reason for the disconnection.
+     * @param imsReasonInfo The IMS reason info.
+     */
+    public void setImsReasonInfo(@Nullable ImsReasonInfo imsReasonInfo) {
+        mImsReasonInfo = imsReasonInfo;
+    }
+
+    /**
+     * @return the {@link ImsReasonInfo} describing why this connection disconnected, or
+     * {@code null} otherwise.
+     */
+    public @Nullable ImsReasonInfo getImsReasonInfo() {
+        return mImsReasonInfo;
+    }
+
+    /**
      * Converts an {@link ImsCallProfile} verification status to a
      * {@link android.telecom.Connection} verification status.
      * @param verificationStatus The {@link ImsCallProfile} verification status.
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index e0f2ff1..2a437f7 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -1382,7 +1382,8 @@
             } else if (ar.exception instanceof ImsException) {
                 sb.append(getImsErrorMessage(ar));
             }
-        } else if (ar.result != null && (int) ar.result == CommandsInterface.SS_STATUS_UNKNOWN) {
+        } else if (ar.result != null && ar.result instanceof Integer
+                && (int) ar.result == CommandsInterface.SS_STATUS_UNKNOWN) {
             mState = State.FAILED;
             sb = null;
         } else if (isActivate()) {
diff --git a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
index 9074a7b..410afcf 100644
--- a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
@@ -36,7 +36,7 @@
      * @param phone
      */
     public static void onDataStallEvent(@DcTracker.RecoveryAction int recoveryAction,
-            Phone phone) {
+            Phone phone, boolean isRecovered, int durationMillis) {
         if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
             phone = phone.getDefaultPhone();
         }
@@ -50,7 +50,8 @@
         boolean isMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;
 
         TelephonyStatsLog.write(TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED, carrierId, rat,
-                signalStrength, recoveryAction, isOpportunistic, isMultiSim, band);
+                signalStrength, recoveryAction, isOpportunistic, isMultiSim, band, isRecovered,
+                durationMillis);
     }
 
     /** Returns the RAT used for data (including IWLAN). */
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index ce01b7e..6cbfefa 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -41,6 +41,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyStatsLog;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
@@ -186,14 +187,12 @@
             return StatsManager.PULL_SKIP;
         }
 
-        StatsEvent e =
-                StatsEvent.newBuilder()
-                        .setAtomId(SIM_SLOT_STATE)
-                        .writeInt(state.numActiveSlots)
-                        .writeInt(state.numActiveSims)
-                        .writeInt(state.numActiveEsims)
-                        .build();
-        data.add(e);
+        data.add(
+                TelephonyStatsLog.buildStatsEvent(
+                        SIM_SLOT_STATE,
+                        state.numActiveSlots,
+                        state.numActiveSims,
+                        state.numActiveEsims));
         return StatsManager.PULL_SUCCESS;
     }
 
@@ -209,12 +208,7 @@
             rafSupported |= phone.getRadioAccessFamily();
         }
 
-        StatsEvent e =
-                StatsEvent.newBuilder()
-                        .setAtomId(SUPPORTED_RADIO_ACCESS_FAMILY)
-                        .writeLong(rafSupported)
-                        .build();
-        data.add(e);
+        data.add(TelephonyStatsLog.buildStatsEvent(SUPPORTED_RADIO_ACCESS_FAMILY, rafSupported));
         return StatsManager.PULL_SUCCESS;
     }
 
@@ -226,11 +220,7 @@
             // All phones should have the same version of the carrier ID table, so only query the
             // first one.
             int version = phones[0].getCarrierIdListVersion();
-            data.add(
-                    StatsEvent.newBuilder()
-                            .setAtomId(CARRIER_ID_TABLE_VERSION)
-                            .writeInt(version)
-                            .build());
+            data.add(TelephonyStatsLog.buildStatsEvent(CARRIER_ID_TABLE_VERSION, version));
             return StatsManager.PULL_SUCCESS;
         }
     }
@@ -379,208 +369,171 @@
     }
 
     private static StatsEvent buildStatsEvent(CellularDataServiceSwitch serviceSwitch) {
-        return StatsEvent.newBuilder()
-                .setAtomId(CELLULAR_DATA_SERVICE_SWITCH)
-                .writeInt(serviceSwitch.ratFrom)
-                .writeInt(serviceSwitch.ratTo)
-                .writeInt(serviceSwitch.simSlotIndex)
-                .writeBoolean(serviceSwitch.isMultiSim)
-                .writeInt(serviceSwitch.carrierId)
-                .writeInt(serviceSwitch.switchCount)
-                .build();
+        return TelephonyStatsLog.buildStatsEvent(
+                CELLULAR_DATA_SERVICE_SWITCH,
+                serviceSwitch.ratFrom,
+                serviceSwitch.ratTo,
+                serviceSwitch.simSlotIndex,
+                serviceSwitch.isMultiSim,
+                serviceSwitch.carrierId,
+                serviceSwitch.switchCount);
     }
 
     private static StatsEvent buildStatsEvent(CellularServiceState state) {
-        return StatsEvent.newBuilder()
-                .setAtomId(CELLULAR_SERVICE_STATE)
-                .writeInt(state.voiceRat)
-                .writeInt(state.dataRat)
-                .writeInt(state.voiceRoamingType)
-                .writeInt(state.dataRoamingType)
-                .writeBoolean(state.isEndc)
-                .writeInt(state.simSlotIndex)
-                .writeBoolean(state.isMultiSim)
-                .writeInt(state.carrierId)
-                .writeInt(
-                        (int)
-                                (round(state.totalTimeMillis, DURATION_BUCKET_MILLIS)
-                                        / SECOND_IN_MILLIS))
-                .build();
+        return TelephonyStatsLog.buildStatsEvent(
+                CELLULAR_SERVICE_STATE,
+                state.voiceRat,
+                state.dataRat,
+                state.voiceRoamingType,
+                state.dataRoamingType,
+                state.isEndc,
+                state.simSlotIndex,
+                state.isMultiSim,
+                state.carrierId,
+                (int) (round(state.totalTimeMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
     }
 
     private static StatsEvent buildStatsEvent(VoiceCallRatUsage usage) {
-        return StatsEvent.newBuilder()
-                .setAtomId(VOICE_CALL_RAT_USAGE)
-                .writeInt(usage.carrierId)
-                .writeInt(usage.rat)
-                .writeLong(
-                        round(usage.totalDurationMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS)
-                .writeLong(usage.callCount)
-                .build();
+        return TelephonyStatsLog.buildStatsEvent(
+                VOICE_CALL_RAT_USAGE,
+                usage.carrierId,
+                usage.rat,
+                round(usage.totalDurationMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS,
+                usage.callCount);
     }
 
     private static StatsEvent buildStatsEvent(VoiceCallSession session) {
-        return StatsEvent.newBuilder()
-                .setAtomId(VOICE_CALL_SESSION)
-                .writeInt(session.bearerAtStart)
-                .writeInt(session.bearerAtEnd)
-                .writeInt(session.direction)
-                .writeInt(session.setupDuration)
-                .writeBoolean(session.setupFailed)
-                .writeInt(session.disconnectReasonCode)
-                .writeInt(session.disconnectExtraCode)
-                .writeString(session.disconnectExtraMessage)
-                .writeInt(session.ratAtStart)
-                .writeInt(session.ratAtEnd)
-                .writeLong(session.ratSwitchCount)
-                .writeLong(session.codecBitmask)
-                .writeInt(session.concurrentCallCountAtStart)
-                .writeInt(session.concurrentCallCountAtEnd)
-                .writeInt(session.simSlotIndex)
-                .writeBoolean(session.isMultiSim)
-                .writeBoolean(session.isEsim)
-                .writeInt(session.carrierId)
-                .writeBoolean(session.srvccCompleted)
-                .writeLong(session.srvccFailureCount)
-                .writeLong(session.srvccCancellationCount)
-                .writeBoolean(session.rttEnabled)
-                .writeBoolean(session.isEmergency)
-                .writeBoolean(session.isRoaming)
+        return TelephonyStatsLog.buildStatsEvent(
+                VOICE_CALL_SESSION,
+                session.bearerAtStart,
+                session.bearerAtEnd,
+                session.direction,
+                session.setupDuration,
+                session.setupFailed,
+                session.disconnectReasonCode,
+                session.disconnectExtraCode,
+                session.disconnectExtraMessage,
+                session.ratAtStart,
+                session.ratAtEnd,
+                session.ratSwitchCount,
+                session.codecBitmask,
+                session.concurrentCallCountAtStart,
+                session.concurrentCallCountAtEnd,
+                session.simSlotIndex,
+                session.isMultiSim,
+                session.isEsim,
+                session.carrierId,
+                session.srvccCompleted,
+                session.srvccFailureCount,
+                session.srvccCancellationCount,
+                session.rttEnabled,
+                session.isEmergency,
+                session.isRoaming,
                 // workaround: dimension required for keeping multiple pulled atoms
-                .writeInt(sRandom.nextInt())
+                sRandom.nextInt(),
                 // New fields introduced in Android S
-                .writeInt(session.signalStrengthAtEnd)
-                .writeInt(session.bandAtEnd)
-                .writeInt(session.setupDurationMillis)
-                .writeInt(session.mainCodecQuality)
-                .writeBoolean(session.videoEnabled)
-                .writeInt(session.ratAtConnected)
-                .writeBoolean(session.isMultiparty)
-                .build();
+                session.signalStrengthAtEnd,
+                session.bandAtEnd,
+                session.setupDurationMillis,
+                session.mainCodecQuality,
+                session.videoEnabled,
+                session.ratAtConnected,
+                session.isMultiparty);
     }
 
     private static StatsEvent buildStatsEvent(IncomingSms sms) {
-        return StatsEvent.newBuilder()
-                .setAtomId(INCOMING_SMS)
-                .writeInt(sms.smsFormat)
-                .writeInt(sms.smsTech)
-                .writeInt(sms.rat)
-                .writeInt(sms.smsType)
-                .writeInt(sms.totalParts)
-                .writeInt(sms.receivedParts)
-                .writeBoolean(sms.blocked)
-                .writeInt(sms.error)
-                .writeBoolean(sms.isRoaming)
-                .writeInt(sms.simSlotIndex)
-                .writeBoolean(sms.isMultiSim)
-                .writeBoolean(sms.isEsim)
-                .writeInt(sms.carrierId)
-                .writeLong(sms.messageId)
-                .build();
+        return TelephonyStatsLog.buildStatsEvent(
+                INCOMING_SMS,
+                sms.smsFormat,
+                sms.smsTech,
+                sms.rat,
+                sms.smsType,
+                sms.totalParts,
+                sms.receivedParts,
+                sms.blocked,
+                sms.error,
+                sms.isRoaming,
+                sms.simSlotIndex,
+                sms.isMultiSim,
+                sms.isEsim,
+                sms.carrierId,
+                sms.messageId);
     }
 
     private static StatsEvent buildStatsEvent(OutgoingSms sms) {
-        return StatsEvent.newBuilder()
-                .setAtomId(OUTGOING_SMS)
-                .writeInt(sms.smsFormat)
-                .writeInt(sms.smsTech)
-                .writeInt(sms.rat)
-                .writeInt(sms.sendResult)
-                .writeInt(sms.errorCode)
-                .writeBoolean(sms.isRoaming)
-                .writeBoolean(sms.isFromDefaultApp)
-                .writeInt(sms.simSlotIndex)
-                .writeBoolean(sms.isMultiSim)
-                .writeBoolean(sms.isEsim)
-                .writeInt(sms.carrierId)
-                .writeLong(sms.messageId)
-                .writeInt(sms.retryId)
-                .build();
+        return TelephonyStatsLog.buildStatsEvent(
+                OUTGOING_SMS,
+                sms.smsFormat,
+                sms.smsTech,
+                sms.rat,
+                sms.sendResult,
+                sms.errorCode,
+                sms.isRoaming,
+                sms.isFromDefaultApp,
+                sms.simSlotIndex,
+                sms.isMultiSim,
+                sms.isEsim,
+                sms.carrierId,
+                sms.messageId,
+                sms.retryId);
     }
 
     private static StatsEvent buildStatsEvent(DataCallSession dataCallSession) {
-        return StatsEvent.newBuilder()
-                .setAtomId(DATA_CALL_SESSION)
-                .writeInt(dataCallSession.dimension)
-                .writeBoolean(dataCallSession.isMultiSim)
-                .writeBoolean(dataCallSession.isEsim)
-                .writeInt(0) // profile is deprecated, so we default to 0
-                .writeInt(dataCallSession.apnTypeBitmask)
-                .writeInt(dataCallSession.carrierId)
-                .writeBoolean(dataCallSession.isRoaming)
-                .writeInt(dataCallSession.ratAtEnd)
-                .writeBoolean(dataCallSession.oosAtEnd)
-                .writeLong(dataCallSession.ratSwitchCount)
-                .writeBoolean(dataCallSession.isOpportunistic)
-                .writeInt(dataCallSession.ipType)
-                .writeBoolean(dataCallSession.setupFailed)
-                .writeInt(dataCallSession.failureCause)
-                .writeInt(dataCallSession.suggestedRetryMillis)
-                .writeInt(dataCallSession.deactivateReason)
-                .writeLong(round(
-                        dataCallSession.durationMinutes, DURATION_BUCKET_MILLIS / MINUTE_IN_MILLIS))
-                .writeBoolean(dataCallSession.ongoing)
-                .writeInt(dataCallSession.bandAtEnd)
-                .build();
+        return TelephonyStatsLog.buildStatsEvent(
+                DATA_CALL_SESSION,
+                dataCallSession.dimension,
+                dataCallSession.isMultiSim,
+                dataCallSession.isEsim,
+                0, // profile is deprecated, so we default to 0
+                dataCallSession.apnTypeBitmask,
+                dataCallSession.carrierId,
+                dataCallSession.isRoaming,
+                dataCallSession.ratAtEnd,
+                dataCallSession.oosAtEnd,
+                dataCallSession.ratSwitchCount,
+                dataCallSession.isOpportunistic,
+                dataCallSession.ipType,
+                dataCallSession.setupFailed,
+                dataCallSession.failureCause,
+                dataCallSession.suggestedRetryMillis,
+                dataCallSession.deactivateReason,
+                round(dataCallSession.durationMinutes, DURATION_BUCKET_MILLIS / MINUTE_IN_MILLIS),
+                dataCallSession.ongoing,
+                dataCallSession.bandAtEnd);
     }
 
     private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) {
-        return StatsEvent.newBuilder()
-                .setAtomId(IMS_REGISTRATION_STATS)
-                .writeInt(stats.carrierId)
-                .writeInt(stats.simSlotIndex)
-                .writeInt(stats.rat)
-                .writeInt(
-                        (int)
-                                (round(stats.registeredMillis, DURATION_BUCKET_MILLIS)
-                                        / SECOND_IN_MILLIS))
-                .writeInt(
-                        (int)
-                                (round(stats.voiceCapableMillis, DURATION_BUCKET_MILLIS)
-                                        / SECOND_IN_MILLIS))
-                .writeInt(
-                        (int)
-                                (round(stats.voiceAvailableMillis, DURATION_BUCKET_MILLIS)
-                                        / SECOND_IN_MILLIS))
-                .writeInt(
-                        (int)
-                                (round(stats.smsCapableMillis, DURATION_BUCKET_MILLIS)
-                                        / SECOND_IN_MILLIS))
-                .writeInt(
-                        (int)
-                                (round(stats.smsAvailableMillis, DURATION_BUCKET_MILLIS)
-                                        / SECOND_IN_MILLIS))
-                .writeInt(
-                        (int)
-                                (round(stats.videoCapableMillis, DURATION_BUCKET_MILLIS)
-                                        / SECOND_IN_MILLIS))
-                .writeInt(
-                        (int)
-                                (round(stats.videoAvailableMillis, DURATION_BUCKET_MILLIS)
-                                        / SECOND_IN_MILLIS))
-                .writeInt(
-                        (int)
-                                (round(stats.utCapableMillis, DURATION_BUCKET_MILLIS)
-                                        / SECOND_IN_MILLIS))
-                .writeInt(
-                        (int)
-                                (round(stats.utAvailableMillis, DURATION_BUCKET_MILLIS)
-                                        / SECOND_IN_MILLIS))
-                .build();
+        return TelephonyStatsLog.buildStatsEvent(
+                IMS_REGISTRATION_STATS,
+                stats.carrierId,
+                stats.simSlotIndex,
+                stats.rat,
+                (int) (round(stats.registeredMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+                (int) (round(stats.voiceCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+                (int)
+                        (round(stats.voiceAvailableMillis, DURATION_BUCKET_MILLIS)
+                                / SECOND_IN_MILLIS),
+                (int) (round(stats.smsCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+                (int) (round(stats.smsAvailableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+                (int) (round(stats.videoCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+                (int)
+                        (round(stats.videoAvailableMillis, DURATION_BUCKET_MILLIS)
+                                / SECOND_IN_MILLIS),
+                (int) (round(stats.utCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+                (int) (round(stats.utAvailableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
     }
 
     private static StatsEvent buildStatsEvent(ImsRegistrationTermination termination) {
-        return StatsEvent.newBuilder()
-                .setAtomId(IMS_REGISTRATION_TERMINATION)
-                .writeInt(termination.carrierId)
-                .writeBoolean(termination.isMultiSim)
-                .writeInt(termination.ratAtEnd)
-                .writeBoolean(termination.setupFailed)
-                .writeInt(termination.reasonCode)
-                .writeInt(termination.extraCode)
-                .writeString(termination.extraMessage)
-                .writeInt(termination.count)
-                .build();
+        return TelephonyStatsLog.buildStatsEvent(
+                IMS_REGISTRATION_TERMINATION,
+                termination.carrierId,
+                termination.isMultiSim,
+                termination.ratAtEnd,
+                termination.setupFailed,
+                termination.reasonCode,
+                termination.extraCode,
+                termination.extraMessage,
+                termination.count);
     }
 
     /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
diff --git a/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java b/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java
index 67816c9..ad27acb 100644
--- a/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java
@@ -71,7 +71,7 @@
             m.rxTimeMs = stats.getRxTimeMillis();
 
             List<Long> txTimeMillis = new ArrayList<>();
-            for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+            for (int i = 0; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
                 long t = stats.getTxTimeMillis(i);
                 if (t >= 0) {
                     txTimeMillis.add(t);
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 9487a13..d1e7ad2 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -78,6 +78,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.SmsController;
 import com.android.internal.telephony.SmsResponse;
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
@@ -1872,7 +1873,7 @@
                     SmsSession.Event.Type.SMS_SEND_RESULT)
                     .setImsServiceErrno(resultCode)
                     .setErrorCode(errorReason)
-                    .setMessageId((messageId))
+                    .setMessageId(messageId)
             );
 
             smsSession.decreaseExpectedResponse();
@@ -2441,7 +2442,7 @@
                 + " parts, source = " + smsSource
                 + " blocked = " + blocked
                 + " type = " + type
-                + " messageId = " + messageId);
+                + " " + SmsController.formatCrossStackMessageId(messageId));
 
         int smsFormat = convertSmsFormat(format);
         int smsError =
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index 342ddb8..f3c1839 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -26,7 +26,7 @@
 import android.telephony.SignalThresholdInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataProfile;
-import android.telephony.data.SliceInfo;
+import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.TrafficDescriptor;
 import android.telephony.emergency.EmergencyNumber;
 
@@ -287,8 +287,8 @@
     @Override
     public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
             boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
-            Message result) {
+            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+            boolean matchAllRuleAllowed, Message result) {
     }
 
     @Override
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
index 319d200..80167d6 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
@@ -248,6 +248,8 @@
         // add some capabilities
         request.addCapabilitiesToEnableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+        request.addCapabilitiesToEnableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM);
         request.addCapabilitiesToEnableForTech(
                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO
                         | MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkValidatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkValidatorTest.java
index 0601384..2c1e18b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkValidatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkValidatorTest.java
@@ -57,9 +57,9 @@
 public class CellularNetworkValidatorTest extends TelephonyTest {
     private CellularNetworkValidator mValidatorUT;
     private static final PhoneCapability CAPABILITY_WITH_VALIDATION_SUPPORTED =
-            new PhoneCapability(1, 1, 0, null, true);
+            new PhoneCapability(1, 1, null, true, new int[0]);
     private static final PhoneCapability CAPABILITY_WITHOUT_VALIDATION_SUPPORTED =
-            new PhoneCapability(1, 1, 0, null, false);
+            new PhoneCapability(1, 1, null, false, new int[0]);
     private final CellIdentityLte mCellIdentityLte1 = new CellIdentityLte(123, 456, 0, 0, 111);
     private final CellIdentityLte mCellIdentityLte2 = new CellIdentityLte(321, 654, 0, 0, 222);
     @Mock
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index b34807d..b96056a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -33,6 +33,7 @@
 import android.app.DownloadManager;
 import android.app.KeyguardManager;
 import android.app.NotificationManager;
+import android.app.UiModeManager;
 import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -82,6 +83,7 @@
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.test.mock.MockContext;
+import android.util.DisplayMetrics;
 import android.util.Log;
 
 import com.google.common.collect.ArrayListMultimap;
@@ -271,6 +273,7 @@
                 case Context.DISPLAY_SERVICE:
                 case Context.POWER_SERVICE:
                 case Context.PERMISSION_SERVICE:
+                case Context.LEGACY_PERMISSION_SERVICE:
                     // These are final classes so cannot be mocked,
                     // return real services.
                     return TestApplication.getAppContext().getSystemService(name);
@@ -299,18 +302,20 @@
                 return Context.POWER_WHITELIST_MANAGER;
             } else if (serviceClass == SystemConfigManager.class) {
                 return Context.SYSTEM_CONFIG_SERVICE;
-            } else if (serviceClass == CarrierConfigManager.class){
-                return Context.CARRIER_CONFIG_SERVICE;
             } else if (serviceClass == ActivityManager.class) {
                 return Context.ACTIVITY_SERVICE;
+            } else if (serviceClass == LocationManager.class) {
+                return Context.LOCATION_SERVICE;
+            } else if (serviceClass == CarrierConfigManager.class) {
+                return Context.CARRIER_CONFIG_SERVICE;
             } else if (serviceClass == TelephonyManager.class) {
                 return Context.TELEPHONY_SERVICE;
+            } else if (serviceClass == UiModeManager.class) {
+                return Context.UI_MODE_SERVICE;
             } else if (serviceClass == KeyguardManager.class) {
                 return Context.KEYGUARD_SERVICE;
             } else if (serviceClass == VcnManager.class) {
                 return Context.VCN_MANAGEMENT_SERVICE;
-            } else if (serviceClass == LocationManager.class) {
-                return Context.LOCATION_SERVICE;
             }
             return super.getSystemServiceName(serviceClass);
         }
@@ -629,7 +634,6 @@
     // The application context is the most important object this class provides to the system
     // under test.
     private final Context mContext = spy(new FakeContext());
-
     // We then create a spy on the application context allowing standard Mockito-style
     // when(...) logic to be used to add specific little responses where needed.
 
@@ -657,13 +661,14 @@
         mock(TelephonyRegistryManager.class);
     private final SystemConfigManager mSystemConfigManager = mock(SystemConfigManager.class);
     private final PowerWhitelistManager mPowerWhitelistManager = mock(PowerWhitelistManager.class);
+    private final LocationManager mLocationManager = mock(LocationManager.class);
     private final KeyguardManager mKeyguardManager = mock(KeyguardManager.class);
     private final VcnManager mVcnManager = mock(VcnManager.class);
-    private final LocationManager mLocationManager = mock(LocationManager.class);
 
     private final ContentProvider mContentProvider = spy(new FakeContentProvider());
 
     private final Configuration mConfiguration = new Configuration();
+    private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
     private final SharedPreferences mSharedPreferences = PreferenceManager
             .getDefaultSharedPreferences(TestApplication.getAppContext());
     private final MockContentResolver mContentResolver = new MockContentResolver();
@@ -718,6 +723,8 @@
         mConfiguration.locale = Locale.US;
         doReturn(mConfiguration).when(mResources).getConfiguration();
 
+        mDisplayMetrics.density = 2.25f;
+        doReturn(mDisplayMetrics).when(mResources).getDisplayMetrics();
         mContentResolver.addProvider(Settings.AUTHORITY, mContentProvider);
         // Settings caches the provider after first get/set call, this is needed to make sure
         // Settings is using mContentProvider as the cached provider across all tests.
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
index 5a0a6b7..44f3aa5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -32,6 +33,8 @@
 import static java.util.Arrays.asList;
 
 import android.annotation.IntDef;
+import android.app.UiModeManager;
+import android.content.Context;
 import android.content.Intent;
 import android.hardware.radio.V1_5.IndicationFilter;
 import android.net.ConnectivityManager;
@@ -47,6 +50,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -128,6 +132,8 @@
                 STATE_TYPE_CHARGING, STATE_TYPE_SCREEN, STATE_TYPE_TETHERING}
     );
 
+    @Mock
+    UiModeManager mUiModeManager;
     private DeviceStateMonitor mDSM;
     // Given a stateType, return the event type that can change the state
     private int state2Event(@StateType int stateType) {
@@ -155,6 +161,9 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
+        mContextFixture.setSystemService(Context.UI_MODE_SERVICE, mUiModeManager);
+        // We don't even need a mock executor, we just need to not throw.
+        doReturn(null).when(mContextFixture.getTestDouble()).getMainExecutor();
         mDSM = new DeviceStateMonitor(mPhone);
 
         // Initialize with ALL states off
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index 5ec14e6..f2624af 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -115,7 +115,9 @@
                     + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES + " BIGINT DEFAULT -1,"
                     + Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED + " INTEGER DEFAULT 0, "
                     + Telephony.SimInfo.COLUMN_RCS_CONFIG + " BLOB,"
-                    + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS + " TEXT"
+                    + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS + " TEXT,"
+                    + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING + " INTEGER DEFAULT 0,"
+                    + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS + "TEXT"
                     + ");";
         }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index fd0c825..1d03c46 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -133,9 +133,10 @@
         super.setUp(getClass().getSimpleName());
 
         doReturn(false).when(mSST).isDeviceShuttingDown();
+        doReturn(true).when(mImsManager).isVolteEnabledByPlatform();
 
         mPhoneUT = new GsmCdmaPhone(mContext, mSimulatedCommands, mNotifier, true, 0,
-            PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory);
+            PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
         mPhoneUT.setVoiceCallSessionStats(mVoiceCallSessionStats);
         ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mUiccController).registerForIccChanged(eq(mPhoneUT), integerArgumentCaptor.capture(),
@@ -998,7 +999,7 @@
         };
 
         Phone phone = new GsmCdmaPhone(mContext, sc, mNotifier, true, 0,
-                PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory);
+                PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
         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/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index c93de7d..0c7fe8e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.content.Intent;
@@ -367,6 +368,41 @@
 
     @Test
     @SmallTest
+    public void testSimpleDsdsFirstBoot() {
+        // at first boot default is not set
+        doReturn(-1).when(mSubControllerMock).getDefaultDataSubId();
+
+        doReturn(true).when(mPhoneMock1).isUserDataEnabled();
+        doReturn(true).when(mPhoneMock2).isUserDataEnabled();
+        // After initialization, sub 2 should have mobile data off.
+        mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
+        mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
+        mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+        processAllMessages();
+        verify(mDataEnabledSettingsMock1).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataEnabledSettingsMock2).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+
+        // as a result of the above calls, update new values to be returned
+        doReturn(false).when(mPhoneMock1).isUserDataEnabled();
+        doReturn(false).when(mPhoneMock2).isUserDataEnabled();
+
+        Intent intent = captureBroadcastIntent();
+        assertEquals(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED, intent.getAction());
+        assertEquals(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA,
+                intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, -1));
+
+        // Setting default data should not trigger any more setDataEnabled().
+        doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
+        mMultiSimSettingControllerUT.notifyDefaultDataSubChanged();
+        processAllMessages();
+        verify(mDataEnabledSettingsMock1, times(1)).setDataEnabled(anyInt(), anyBoolean());
+        verify(mDataEnabledSettingsMock2, times(1)).setDataEnabled(anyInt(), anyBoolean());
+    }
+
+    @Test
+    @SmallTest
     public void testDsdsGrouping() {
         doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
         doReturn(false).when(mPhoneMock1).isUserDataEnabled();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index d8a0585..96a89c2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -140,7 +140,54 @@
         // NR NSA, millimeter wave frequency
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
         updateOverrideNetworkType();
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+                mNetworkTypeController.getOverrideNetworkType());
+    }
+
+    @Test
+    public void testUpdateOverrideNetworkTypeNrSa() throws Exception {
+        // not NR
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
+        updateOverrideNetworkType();
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+                mNetworkTypeController.getOverrideNetworkType());
+
+        // NR SA connected
+        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .build();
+        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+                anyInt(), anyInt());
+
+        updateOverrideNetworkType();
+
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+                mNetworkTypeController.getOverrideNetworkType());
+    }
+
+    @Test
+    public void testUpdateOverrideNetworkTypeNrSaMmwave() throws Exception {
+        // not NR
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
+        updateOverrideNetworkType();
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+                mNetworkTypeController.getOverrideNetworkType());
+
+        // NR SA connected and millimeter wave frequency
+        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .build();
+        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+                anyInt(), anyInt());
+        doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+
+        updateOverrideNetworkType();
+
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
     }
 
@@ -456,7 +503,7 @@
         broadcastCarrierConfigs();
 
         assertEquals("connected_mmwave", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
 
         // should trigger 10 second timer
@@ -465,7 +512,7 @@
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.is5GHysteresisActive());
 
@@ -489,7 +536,7 @@
         broadcastCarrierConfigs();
 
         assertEquals("connected_mmwave", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
 
         // trigger 10 second timer after disconnecting from NR
@@ -498,7 +545,7 @@
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.is5GHysteresisActive());
 
@@ -512,7 +559,7 @@
 
         // timer should not have gone off
         assertEquals("connected_mmwave", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
         assertFalse(mNetworkTypeController.is5GHysteresisActive());
     }
@@ -622,7 +669,7 @@
         broadcastCarrierConfigs();
 
         assertEquals("connected_mmwave", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
 
         // should trigger 10 second primary timer
@@ -631,7 +678,7 @@
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.is5GHysteresisActive());
 
@@ -641,7 +688,7 @@
 
         // should trigger 30 second secondary timer
         assertEquals("connected", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.is5GHysteresisActive());
 
@@ -667,7 +714,7 @@
         broadcastCarrierConfigs();
 
         assertEquals("connected_mmwave", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
 
         // should trigger 10 second primary timer
@@ -676,7 +723,7 @@
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.is5GHysteresisActive());
 
@@ -686,7 +733,7 @@
 
         // should trigger 30 second secondary timer
         assertEquals("connected", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.is5GHysteresisActive());
 
@@ -700,7 +747,7 @@
 
         // timer should not have gone off
         assertEquals("connected_mmwave", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
         assertFalse(mNetworkTypeController.is5GHysteresisActive());
     }
@@ -717,7 +764,7 @@
         broadcastCarrierConfigs();
 
         assertEquals("connected_mmwave", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
 
         // should trigger 10 second primary timer
@@ -726,7 +773,7 @@
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
-        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.is5GHysteresisActive());
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneCapabilityTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneCapabilityTest.java
index a504dc1..2410625 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneCapabilityTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneCapabilityTest.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
@@ -35,23 +36,25 @@
     public void basicTests() throws Exception {
         int maxActiveVoiceCalls = 1;
         int maxActiveData = 2;
-        int max5G = 3;
         ModemInfo modemInfo = new ModemInfo(1, 2, true, false);
         List<ModemInfo> logicalModemList = new ArrayList<>();
         logicalModemList.add(modemInfo);
+        int[] deviceNrCapabilities = new int[]{PhoneCapability.DEVICE_NR_CAPABILITY_SA};
 
-        PhoneCapability capability = new PhoneCapability(maxActiveVoiceCalls, maxActiveData, max5G,
-                logicalModemList, false);
+        PhoneCapability capability = new PhoneCapability(maxActiveVoiceCalls, maxActiveData,
+                logicalModemList, false, deviceNrCapabilities);
 
-        assertEquals(maxActiveVoiceCalls, capability.maxActiveVoiceCalls);
-        assertEquals(maxActiveData, capability.maxActiveData);
-        assertEquals(max5G, capability.max5G);
-        assertEquals(1, capability.logicalModemList.size());
-        assertEquals(modemInfo, capability.logicalModemList.get(0));
-        PhoneCapability toCompare = new PhoneCapability(
-                maxActiveVoiceCalls + 1, maxActiveData - 1, max5G, logicalModemList, false);
-        assertEquals(capability, new PhoneCapability(
-                maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList, false));
+        assertEquals(maxActiveVoiceCalls, capability.getMaxActiveVoiceSubscriptions());
+        assertEquals(maxActiveData, capability.getMaxActiveDataSubscriptions());
+        assertEquals(1, capability.getLogicalModemList().size());
+        assertEquals(modemInfo, capability.getLogicalModemList().get(0));
+        assertArrayEquals(deviceNrCapabilities, capability.getDeviceNrCapabilities());
+
+        PhoneCapability toCompare = new PhoneCapability(maxActiveVoiceCalls + 1, maxActiveData - 1,
+                logicalModemList, false, deviceNrCapabilities);
+        assertEquals(capability,
+                new PhoneCapability(maxActiveVoiceCalls, maxActiveData, logicalModemList,
+                        false, deviceNrCapabilities));
         assertNotEquals(capability, toCompare);
     }
 
@@ -60,24 +63,24 @@
     public void parcelReadWrite() throws Exception {
         int maxActiveVoiceCalls = 1;
         int maxActiveData = 2;
-        int max5G = 3;
         ModemInfo modemInfo = new ModemInfo(1, 2, true, false);
         List<ModemInfo> logicalModemList = new ArrayList<>();
         logicalModemList.add(modemInfo);
+        int[] deviceNrCapabilities = new int[]{};
 
-        PhoneCapability capability = new PhoneCapability(maxActiveVoiceCalls, maxActiveData, max5G,
-                logicalModemList, false);
+        PhoneCapability capability = new PhoneCapability(maxActiveVoiceCalls, maxActiveData,
+                logicalModemList, false, deviceNrCapabilities);
 
         Parcel parcel = Parcel.obtain();
         capability.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
         PhoneCapability toCompare = PhoneCapability.CREATOR.createFromParcel(parcel);
 
-        assertEquals(maxActiveVoiceCalls, toCompare.maxActiveVoiceCalls);
-        assertEquals(maxActiveData, toCompare.maxActiveData);
-        assertEquals(max5G, toCompare.max5G);
-        assertEquals(1, toCompare.logicalModemList.size());
-        assertEquals(modemInfo, toCompare.logicalModemList.get(0));
+        assertEquals(maxActiveVoiceCalls, toCompare.getMaxActiveVoiceSubscriptions());
+        assertEquals(maxActiveData, toCompare.getMaxActiveDataSubscriptions());
+        assertEquals(1, toCompare.getLogicalModemList().size());
+        assertEquals(modemInfo, toCompare.getLogicalModemList().get(0));
+        assertArrayEquals(deviceNrCapabilities, toCompare.getDeviceNrCapabilities());
         assertEquals(capability, toCompare);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
index e13baca..3485f49 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
@@ -20,6 +20,8 @@
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION;
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
 
 import static com.android.internal.telephony.PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS;
 import static com.android.internal.telephony.PhoneSwitcher.EVENT_DATA_ENABLED_CHANGED;
@@ -56,6 +58,7 @@
 import android.os.Messenger;
 import android.telephony.PhoneCapability;
 import android.telephony.SubscriptionManager;
+import android.telephony.data.ApnSetting;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -84,6 +87,8 @@
     @Mock
     private Phone mPhone2; // mPhone as phone 1 is already defined in TelephonyTest.
     @Mock
+    private Phone mImsPhone;
+    @Mock
     private DataEnabledSettings mDataEnabledSettings2;
     @Mock
     private Handler mActivePhoneSwitchHandler;
@@ -99,6 +104,8 @@
     private ISetOpportunisticDataCallback mSetOpptDataCallback2;
     @Mock
     CompletableFuture<Boolean> mFuturePhone;
+    @Mock
+    PhoneSwitcher.ImsRegTechProvider mMockImsRegTechProvider;
 
     private PhoneSwitcher mPhoneSwitcher;
     private SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
@@ -116,7 +123,7 @@
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
 
-        PhoneCapability phoneCapability = new PhoneCapability(1, 1, 0, null, false);
+        PhoneCapability phoneCapability = new PhoneCapability(1, 1, null, false, new int[0]);
         doReturn(phoneCapability).when(mPhoneConfigurationManager).getCurrentPhoneCapability();
 
         doReturn(Call.State.ACTIVE).when(mActiveCall).getState();
@@ -529,6 +536,66 @@
         assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
     }
 
+    private void mockImsRegTech(int phoneId, int regTech) {
+        doReturn(regTech).when(mMockImsRegTechProvider).get(any(), eq(phoneId));
+        mPhoneSwitcher.mImsRegTechProvider = mMockImsRegTechProvider;
+    }
+
+    @Test
+    @SmallTest
+    public void testNonDefaultDataPhoneInCall_ImsCallOnLte_shouldSwitchDds() throws Exception {
+        initialize();
+        setAllPhonesInactive();
+
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        // Both are active subscriptions are active sub, as they are in both active slots.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+        processAllMessages();
+
+        // Phone 0 should be the default data phoneId.
+        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+
+        // 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(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+        mockImsRegTech(1, REGISTRATION_TECH_LTE);
+        notifyPhoneAsInCall(mImsPhone);
+
+        // Phone 1 should become the default data phone.
+        assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+    }
+
+    @Test
+    @SmallTest
+    public void testNonDefaultDataPhoneInCall_ImsCallOnWlan_shouldNotSwitchDds() throws Exception {
+        initialize();
+        setAllPhonesInactive();
+
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        // Both are active subscriptions are active sub, as they are in both active slots.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+        processAllMessages();
+
+        // Phone 0 should be the default data phoneId.
+        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+
+        // Phone2 has active call, but data is turned off. So no data switching should happen.
+        doReturn(mImsPhone).when(mPhone2).getImsPhone();
+        doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+        mockImsRegTech(1, REGISTRATION_TECH_IWLAN);
+        notifyPhoneAsInCall(mImsPhone);
+
+        // Phone 0 should remain the default data phone.
+        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+    }
+
     @Test
     @SmallTest
     public void testNonDefaultDataPhoneInCall() throws Exception {
@@ -1005,6 +1072,9 @@
         doReturn(mInactiveCall).when(mPhone2).getForegroundCall();
         doReturn(mInactiveCall).when(mPhone2).getBackgroundCall();
         doReturn(mInactiveCall).when(mPhone2).getRingingCall();
+        doReturn(mInactiveCall).when(mImsPhone).getForegroundCall();
+        doReturn(mInactiveCall).when(mImsPhone).getBackgroundCall();
+        doReturn(mInactiveCall).when(mImsPhone).getRingingCall();
     }
 
     private void notifyPhoneAsInCall(Phone phone) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index f4c20f5..7c81bda 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -43,6 +43,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_IMSI;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_RADIO_CAPABILITY;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SIM_STATUS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SLICING_CONFIG;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SMSC_ADDRESS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
@@ -2361,6 +2362,7 @@
 
         response = new DataCallResponse.Builder()
                 .setCause(0)
+                .setRetryDurationMillis(-1L)
                 .setId(0)
                 .setLinkStatus(2)
                 .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
@@ -2820,4 +2822,18 @@
         mRILUnderTest.setCompatVersion(testRequest, RIL.RADIO_HAL_VERSION_1_5);
         assertEquals(RIL.RADIO_HAL_VERSION_1_3, mRILUnderTest.getCompatVersion(testRequest));
     }
+
+    @FlakyTest
+    @Test
+    public void testGetSlicingConfig() throws Exception {
+        // Use Radio HAL v1.6
+        try {
+            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+        } catch (Exception e) {
+        }
+        mRILUnderTest.getSlicingConfig(obtainMessage());
+        verify(mRadioProxy).getSlicingConfig(mSerialNumberCaptor.capture());
+        verifyRILResponse_1_6(
+                mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_SLICING_CONFIG);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java b/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java
index f069262..a346ece 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java
@@ -54,6 +54,8 @@
                         TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE));
         assertFalse(
                 caps.contains(TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING));
+        assertFalse(
+                caps.contains(TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED));
     }
 
     @Test
@@ -68,6 +70,8 @@
                         TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE));
         assertFalse(
                 caps.contains(TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING));
+        assertFalse(
+                caps.contains(TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED));
     }
 
     @Test
@@ -82,5 +86,7 @@
                         TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE));
         assertTrue(
                 caps.contains(TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING));
+        assertTrue(
+                caps.contains(TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED));
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 2a23387..0bef358 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -91,6 +91,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.cdma.CdmaCellLocation;
 import android.telephony.gsm.GsmCellLocation;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
@@ -164,6 +165,7 @@
     private static final String CARRIER_NAME_DISPLAY_NO_SERVICE = "No service";
     private static final String CARRIER_NAME_DISPLAY_EMERGENCY_CALL = "emergency call";
     private static final String WIFI_CALLING_VOICE_FORMAT = "%s wifi calling";
+    private static final String CROSS_SIM_CALLING_VOICE_FORMAT = "%s Cross-SIM Calling";
     private static final String WIFI_CALLING_DATA_FORMAT = "%s wifi data";
     private static final String WIFI_CALLING_FLIGHT_MODE_FORMAT = "%s flight mode";
 
@@ -2595,6 +2597,40 @@
     }
 
     @Test
+    public void testUpdateSpnDisplay_spnNotEmptyAndCrossSimCallingEnabled_showSpnOnly() {
+        // GSM phone
+
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+        // In Service
+        ServiceState ss = new ServiceState();
+        ss.setVoiceRegState(ServiceState.STATE_IN_SERVICE);
+        ss.setDataRegState(ServiceState.STATE_IN_SERVICE);
+        sst.mSS = ss;
+
+        // cross-sim-calling is enable
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM).when(mImsPhone)
+                .getImsRegistrationTech();
+        String[] formats = {CROSS_SIM_CALLING_VOICE_FORMAT, "%s"};
+        Resources r = mContext.getResources();
+        doReturn(formats).when(r).getStringArray(anyInt());
+
+        // update the spn
+        sst.updateSpnDisplay();
+
+        // Only spn should be shown
+        String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
+        Bundle b = getExtrasFromLastSpnUpdateIntent();
+        assertThat(b.getString(TelephonyManager.EXTRA_SPN))
+                .isEqualTo(String.format(CROSS_SIM_CALLING_VOICE_FORMAT, spn));
+        assertThat(b.getBoolean(TelephonyManager.EXTRA_SHOW_SPN)).isTrue();
+        assertThat(b.getString(TelephonyManager.EXTRA_DATA_SPN))
+                .isEqualTo(String.format(CROSS_SIM_CALLING_VOICE_FORMAT, spn));
+        assertThat(b.getBoolean(TelephonyManager.EXTRA_SHOW_PLMN)).isFalse();
+    }
+
+    @Test
     public void testUpdateSpnDisplay_spnNotEmptyAndWifiCallingEnabled_showSpnOnly() {
         // GSM phone
         doReturn(true).when(mPhone).isPhoneTypeGsm();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
index a64de8c..40fa229 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
@@ -49,7 +49,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
-import android.telephony.data.SliceInfo;
+import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.TrafficDescriptor;
 import android.telephony.emergency.EmergencyNumber;
 
@@ -1188,8 +1188,8 @@
     @Override
     public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
             boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
-            Message result) {
+            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+            boolean matchAllRuleAllowed, Message result) {
 
         SimulatedCommandsVerifier.getInstance().setupDataCall(accessNetworkType, dataProfile,
                 isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
@@ -2436,6 +2436,12 @@
         resultSuccess(message, null);
     }
 
+    @Override
+    public void getSlicingConfig(Message result) {
+        SimulatedCommandsVerifier.getInstance().getSlicingConfig(result);
+        resultSuccess(result, null);
+    }
+
     @VisibleForTesting
     public void setDataRegStateResult(Object regStateResult) {
         mDataRegStateResult = regStateResult;
@@ -2445,4 +2451,5 @@
     public void setVoiceRegStateResult(Object regStateResult) {
         mVoiceRegStateResult = regStateResult;
     }
+
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
index 5d40fe6..c4d54ff 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
@@ -26,7 +26,7 @@
 import android.telephony.SignalThresholdInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataProfile;
-import android.telephony.data.SliceInfo;
+import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.TrafficDescriptor;
 import android.telephony.emergency.EmergencyNumber;
 
@@ -1204,8 +1204,8 @@
     @Override
     public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
             boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
-            Message result) {
+            NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+            boolean matchAllRuleAllowed, Message result) {
     }
 
     @Override
@@ -1492,4 +1492,8 @@
     @Override
     public void releasePduSessionId(Message result, int pduSessionId) {
     }
+
+    @Override
+    public void getSlicingConfig(Message result) {
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index 6566d3d..2daeaea 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -47,6 +47,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
@@ -112,6 +113,10 @@
     public void setUp() throws Exception {
         super.setUp("SubscriptionControllerTest");
 
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
         doReturn(SINGLE_SIM).when(mTelephonyManager).getSimCount();
         doReturn(SINGLE_SIM).when(mTelephonyManager).getPhoneCount();
         mMockContentResolver = (MockContentResolver) mContext.getContentResolver();
@@ -144,8 +149,10 @@
 
         /*clear sub info in mSubscriptionControllerUT since they will otherwise be persistent
          * between each test case. */
-        mSubscriptionControllerUT.clearSubInfo();
-        mSubscriptionControllerUT.resetStaticMembers();
+        if (mSubscriptionControllerUT != null) {
+            mSubscriptionControllerUT.clearSubInfo();
+            mSubscriptionControllerUT.resetStaticMembers();
+        }
 
         /* clear settings for default voice/data/sms sub ID */
         Settings.Global.putInt(mContext.getContentResolver(),
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index f9199f7..5b8be32 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -88,6 +88,7 @@
     private static final String FAKE_ICCID_1 = "89012604200000000000";
     private static final String FAKE_MCC_MNC_1 = "123456";
     private static final String FAKE_MCC_MNC_2 = "456789";
+    private static final int FAKE_PHONE_ID_1 = 0;
 
     private SubscriptionInfoUpdater mUpdater;
     private IccRecords mIccRecord;
@@ -239,17 +240,17 @@
     @SmallTest
     public void testSimNotReady() throws Exception {
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_SUB_ID_1);
+                IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_PHONE_ID_1);
 
         processAllMessages();
         assertFalse(mUpdater.isSubInfoInitialized());
         verify(mSubscriptionContent, never()).put(anyString(), any());
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager, never()).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
+        verify(mConfigManager, never()).updateConfigForPhoneId(eq(FAKE_PHONE_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_NOT_READY));
-        verify(mSubscriptionController, never()).clearSubInfo();
-        verify(mSubscriptionController, never()).notifySubscriptionInfoChanged();
+        verify(mSubscriptionController).clearSubInfoRecord(FAKE_PHONE_ID_1);
+        verify(mSubscriptionController).notifySubscriptionInfoChanged();
     }
 
     @Test
@@ -259,19 +260,19 @@
         doReturn(true).when(mIccCard).isEmptyProfile();
 
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_SUB_ID_1);
+                IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_PHONE_ID_1);
 
         processAllMessages();
         assertTrue(mUpdater.isSubInfoInitialized());
         // Sub info should be cleared and change should be notified.
-        verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_SUB_ID_1));
+        verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_PHONE_ID_1));
         verify(mSubscriptionController).notifySubscriptionInfoChanged();
         // No new sub should be added.
         verify(mSubscriptionManager, never()).addSubscriptionInfoRecord(any(), anyInt());
         verify(mSubscriptionContent, never()).put(anyString(), any());
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
+        verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_PHONE_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_NOT_READY));
     }
 
@@ -286,24 +287,24 @@
         doReturn(false).when(mSubInfo).areUiccApplicationsEnabled();
 
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_SUB_ID_1);
+                IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_PHONE_ID_1);
 
         processAllMessages();
         assertTrue(mUpdater.isSubInfoInitialized());
         // Sub info should be cleared and change should be notified.
-        verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_SUB_ID_1));
+        verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_PHONE_ID_1));
         verify(mSubscriptionController).notifySubscriptionInfoChanged();
         // No new sub should be added.
         verify(mSubscriptionManager, never()).addSubscriptionInfoRecord(any(), anyInt());
         verify(mSubscriptionContent, never()).put(anyString(), any());
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
+        verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_PHONE_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_NOT_READY));
 
         // When becomes ABSENT, UICC_APPLICATIONS_ENABLED should be reset to true.
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1);
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_PHONE_ID_1);
         processAllMessages();
         ArgumentCaptor<ContentValues> valueCapture = ArgumentCaptor.forClass(ContentValues.class);
         verify(mContentProvider).update(eq(SubscriptionManager.CONTENT_URI), valueCapture.capture(),
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
index dbaa29a..6ca706e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
@@ -36,7 +36,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ServiceManager;
-import android.permission.PermissionManager;
+import android.permission.LegacyPermissionManager;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
@@ -46,7 +46,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.LegacyPermissionManagerService;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -88,7 +88,7 @@
     @Mock
     private TelephonyManager mTelephonyManagerMockForSub2;
     @Mock
-    private PermissionManagerService mMockPermissionManagerService;
+    private LegacyPermissionManagerService mMockLegacyPermissionManagerService;
 
     private MockContentResolver mMockContentResolver;
     private FakeSettingsConfigProvider mFakeSettingsConfigProvider;
@@ -107,10 +107,10 @@
         when(mMockSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(
                 new int[]{SUB_ID});
 
-        PermissionManager permissionManager = new PermissionManager(mMockContext, null,
-                mMockPermissionManagerService);
-        when(mMockContext.getSystemService(Context.PERMISSION_SERVICE)).thenReturn(
-                permissionManager);
+        LegacyPermissionManager legacyPermissionManager = new LegacyPermissionManager(
+                mMockLegacyPermissionManagerService);
+        when(mMockContext.getSystemService(Context.LEGACY_PERMISSION_SERVICE)).thenReturn(
+                legacyPermissionManager);
 
         // By default, assume we have no permissions or app-ops bits.
         doThrow(new SecurityException()).when(mMockContext)
@@ -325,8 +325,8 @@
         // performed by a SystemAPI in PermissionManager; this test verifies when this API returns
         // the calling package meets the requirements for device identifier access the telephony
         // check also returns true.
-        when(mMockPermissionManagerService.checkDeviceIdentifierAccess(PACKAGE, MSG, FEATURE, PID,
-                UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mMockLegacyPermissionManagerService.checkDeviceIdentifierAccess(PACKAGE, MSG, FEATURE,
+                PID, UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
         assertTrue(
                 TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
                         SUB_ID, PACKAGE, FEATURE, MSG));
@@ -616,7 +616,7 @@
                 android.Manifest.permission.READ_DEVICE_CONFIG)).thenReturn(
                 PackageManager.PERMISSION_GRANTED);
 
-        when(mMockPermissionManagerService.checkDeviceIdentifierAccess(any(), any(), any(),
+        when(mMockLegacyPermissionManagerService.checkDeviceIdentifierAccess(any(), any(), any(),
                 anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
 
         // TelephonyPermissions queries DeviceConfig to determine if the identifier access
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index 44dc4c7..1362b33 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -29,7 +29,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.anyString;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -65,7 +65,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -92,6 +91,7 @@
     private int mSrvccState = -1;
     private int mRadioPowerState = RADIO_POWER_UNAVAILABLE;
     private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+    private TelephonyRegistry.ConfigurationProvider mMockConfigurationProvider;
     private CellLocation mCellLocation;
 
     // All events contribute to TelephonyRegistry#isPhoneStatePermissionRequired
@@ -198,18 +198,18 @@
         @Override
         public void onLinkCapacityEstimateChanged(
                 List<LinkCapacityEstimate> linkCapacityEstimateList) {
-            mLinkCapacityEstimateList =  linkCapacityEstimateList;
-        }
-
-        @Override
-        public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs) {
-            mPhysicalChannelConfigs = configs;
+            mLinkCapacityEstimateList = linkCapacityEstimateList;
         }
 
         @Override
         public void onCellLocationChanged(CellLocation location) {
             mCellLocation = location;
         }
+
+        @Override
+        public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs) {
+            mPhysicalChannelConfigs = configs;
+        }
     }
 
     private void addTelephonyRegistryService() {
@@ -227,12 +227,13 @@
     @Before
     public void setUp() throws Exception {
         super.setUp("TelephonyRegistryTest");
-        TelephonyRegistry.ConfigurationProvider mockConfigurationProvider =
-                mock(TelephonyRegistry.ConfigurationProvider.class);
-        when(mockConfigurationProvider.getRegistrationLimit()).thenReturn(-1);
-        when(mockConfigurationProvider.isRegistrationLimitEnabledInPlatformCompat(anyInt()))
+        mMockConfigurationProvider = mock(TelephonyRegistry.ConfigurationProvider.class);
+        when(mMockConfigurationProvider.getRegistrationLimit()).thenReturn(-1);
+        when(mMockConfigurationProvider.isRegistrationLimitEnabledInPlatformCompat(anyInt()))
                 .thenReturn(false);
-        mTelephonyRegistry = new TelephonyRegistry(mContext, mockConfigurationProvider);
+        when(mMockConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(
+                anyString(), any())).thenReturn(false);
+        mTelephonyRegistry = new TelephonyRegistry(mContext, mMockConfigurationProvider);
         addTelephonyRegistryService();
         mTelephonyCallback = new TelephonyCallbackWrapper();
         mTelephonyCallback.init(mSimpleExecutor);
@@ -252,7 +253,7 @@
         doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
         doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
         // mTelephonyRegistry.listen with notifyNow = true should trigger callback immediately.
-        PhoneCapability phoneCapability = new PhoneCapability(1, 2, 3, null, false);
+        PhoneCapability phoneCapability = new PhoneCapability(1, 2, null, false, new int[0]);
         int[] events = {TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED};
         mTelephonyRegistry.notifyPhoneCapabilityChanged(phoneCapability);
         mTelephonyRegistry.listenWithEventList(0, mContext.getOpPackageName(),
@@ -261,7 +262,7 @@
         assertEquals(phoneCapability, mPhoneCapability);
 
         // notifyPhoneCapabilityChanged with a new capability. Callback should be triggered.
-        phoneCapability = new PhoneCapability(3, 2, 2, null, false);
+        phoneCapability = new PhoneCapability(3, 2, null, false, new int[0]);
         mTelephonyRegistry.notifyPhoneCapabilityChanged(phoneCapability);
         processAllMessages();
         assertEquals(phoneCapability, mPhoneCapability);
@@ -487,19 +488,41 @@
         assertSecurityExceptionThrown(
                 READ_PHONE_STATE_EVENTS.stream().mapToInt(i -> i).toArray());
 
-        // Grant permssion
+        // Grant permission
         mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE);
         assertSecurityExceptionNotThrown(
                 READ_PHONE_STATE_EVENTS.stream().mapToInt(i -> i).toArray());
     }
 
     /**
+     * Test enforcement of READ_PHONE_STATE for call state related events.
+     */
+    @Test
+    public void testCallStateChangedPermission() {
+        int[] events = new int[] {TelephonyCallback.EVENT_CALL_STATE_CHANGED,
+                TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED};
+        // Disable change ID for READ_PHONE_STATE enforcement
+        when(mMockConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(
+                anyString(), any())).thenReturn(false);
+        // Start without READ_PHONE_STATE permission
+        mContextFixture.addCallingOrSelfPermission("");
+        assertSecurityExceptionNotThrown(events);
+        // Grant permission
+        mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE);
+        assertSecurityExceptionNotThrown(events);
+        //Enable READ_PHONE_STATE enforcement
+        when(mMockConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(
+                anyString(), any())).thenReturn(true);
+        assertSecurityExceptionNotThrown(events);
+        // revoke READ_PHONE_STATE permission
+        mContextFixture.removeCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE);
+        assertSecurityExceptionThrown(events);
+
+    }
+
+    /**
      * Test listen to events that require READ_PRECISE_PHONE_STATE permission.
      */
-    // FIXME(b/159082270) - Simply not granting location permission doesn't fix the test because
-    // Location is soft-denied to apps that aren't in the foreground, and soft-denial currently
-    // short-circuits the test.
-    @Ignore("Skip due to b/159082270")
     @Test
     public void testReadPrecisePhoneStatePermission() {
         // Clear all permission grants for test.
@@ -514,7 +537,7 @@
         assertSecurityExceptionThrown(
                 READ_PRECISE_PHONE_STATE_EVENTS.stream().mapToInt(i -> i).toArray());
 
-        // Grant permssion
+        // Grant permission
         mContextFixture.addCallingOrSelfPermission(
                 android.Manifest.permission.READ_PRECISE_PHONE_STATE);
         assertSecurityExceptionNotThrown(
@@ -555,7 +578,7 @@
         assertSecurityExceptionThrown(
                 READ_PRIVILEGED_PHONE_STATE_EVENTS.stream().mapToInt(i -> i).toArray());
 
-        // Grant permssion
+        // Grant permission
         mContextFixture.addCallingOrSelfPermission(
                 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
         assertSecurityExceptionNotThrown(
@@ -572,7 +595,7 @@
         assertSecurityExceptionThrown(
                 READ_ACTIVE_EMERGENCY_SESSION_EVENTS.stream().mapToInt(i -> i).toArray());
 
-        // Grant permssion
+        // Grant permission
         mContextFixture.addCallingOrSelfPermission(
                 android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION);
         assertSecurityExceptionNotThrown(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index ab076d8..b1f0e2e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -58,7 +58,7 @@
 import android.os.RegistrantList;
 import android.os.ServiceManager;
 import android.os.UserManager;
-import android.permission.PermissionManager;
+import android.permission.LegacyPermissionManager;
 import android.provider.BlockedNumberContract;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
@@ -88,6 +88,7 @@
 import com.android.internal.telephony.dataconnection.DataEnabledSettings;
 import com.android.internal.telephony.dataconnection.DataThrottler;
 import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
 import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
@@ -112,6 +113,7 @@
 import com.android.internal.telephony.uicc.UiccProfile;
 import com.android.internal.telephony.uicc.UiccSlot;
 import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.permission.LegacyPermissionManagerService;
 import com.android.server.pm.permission.PermissionManagerService;
 
 import org.mockito.Mock;
@@ -208,6 +210,8 @@
     @Mock
     protected PackageManagerService mMockPackageManager;
     @Mock
+    protected LegacyPermissionManagerService mMockLegacyPermissionManager;
+    @Mock
     protected PermissionManagerService mMockPermissionManager;
 
     protected NetworkRegistrationInfo mNetworkRegistrationInfo =
@@ -320,6 +324,8 @@
     @Mock
     protected ImsStats mImsStats;
     @Mock
+    protected LinkBandwidthEstimator mLinkBandwidthEstimator;
+    @Mock
     protected PinStorage mPinStorage;
     @Mock
     protected LocationManager mLocationManager;
@@ -534,6 +540,8 @@
                 .makeDataEnabledSettings(nullable(Phone.class));
         doReturn(mEriManager).when(mTelephonyComponentFactory)
                 .makeEriManager(nullable(Phone.class), anyInt());
+        doReturn(mLinkBandwidthEstimator).when(mTelephonyComponentFactory)
+                .makeLinkBandwidthEstimator(nullable(Phone.class));
 
         //mPhone
         doReturn(mContext).when(mPhone).getContext();
@@ -564,6 +572,7 @@
         doReturn(mSmsStats).when(mPhone).getSmsStats();
         doReturn(mImsStats).when(mImsPhone).getImsStats();
         mIccSmsInterfaceManager.mDispatchersController = mSmsDispatchersController;
+        doReturn(mLinkBandwidthEstimator).when(mPhone).getLinkBandwidthEstimator();
 
         //mUiccController
         doReturn(mUiccCardApplication3gpp).when(mUiccController).getUiccCardApplication(anyInt(),
@@ -640,6 +649,7 @@
         mSST.mRestrictedState = mRestrictedState;
         mServiceManagerMockedServices.put("connectivity_metrics_logger", mConnMetLoggerBinder);
         mServiceManagerMockedServices.put("package", mMockPackageManager);
+        mServiceManagerMockedServices.put("legacy_permission", mMockLegacyPermissionManager);
         mServiceManagerMockedServices.put("permissionmgr", mMockPermissionManager);
         logd("mMockPermissionManager replaced");
         doReturn(new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
@@ -850,12 +860,21 @@
 
         // TelephonyPermissions uses a SystemAPI to check if the calling package meets any of the
         // generic requirements for device identifier access (currently READ_PRIVILEGED_PHONE_STATE,
-        // appop, and device / profile owner checks. This sets up the PermissionManager to return
+        // appop, and device / profile owner checks). This sets up the PermissionManager to return
         // that access requirements are met.
         setIdentifierAccess(true);
-        PermissionManager permissionManager = new PermissionManager(mContext, null,
-                mMockPermissionManager);
-        doReturn(permissionManager).when(mContext).getSystemService(eq(Context.PERMISSION_SERVICE));
+        LegacyPermissionManager legacyPermissionManager =
+                new LegacyPermissionManager(mMockLegacyPermissionManager);
+        doReturn(legacyPermissionManager).when(mContext)
+                .getSystemService(Context.LEGACY_PERMISSION_SERVICE);
+        // Also make sure all appop checks fails, to not interfere tests. Tests should explicitly
+        // mock AppOpManager to return allowed/default mode. Note by default a mock returns 0 which
+        // is MODE_ALLOWED, hence this setup is necessary.
+        doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager).noteOpNoThrow(
+                /* op= */ anyString(), /* uid= */ anyInt(),
+                /* packageName= */ nullable(String.class),
+                /* attributionTag= */ nullable(String.class),
+                /* message= */ nullable(String.class));
 
         // TelephonyPermissions queries DeviceConfig to determine if the identifier access
         // restrictions should be enabled; this results in a NPE when DeviceConfig uses
@@ -882,9 +901,8 @@
 
     protected void setIdentifierAccess(boolean hasAccess) {
         doReturn(hasAccess ? PackageManager.PERMISSION_GRANTED
-                : PackageManager.PERMISSION_DENIED).when(
-                mMockPermissionManager).checkDeviceIdentifierAccess(any(), any(), any(), anyInt(),
-                anyInt());
+                : PackageManager.PERMISSION_DENIED).when(mMockLegacyPermissionManager)
+                .checkDeviceIdentifierAccess(any(), any(), any(), anyInt(), anyInt());
     }
 
     protected void setCarrierPrivileges(boolean hasCarrierPrivileges) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/d2d/CommunicatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/d2d/CommunicatorTest.java
index 2ebb1bc..40abd53 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/d2d/CommunicatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/d2d/CommunicatorTest.java
@@ -57,7 +57,6 @@
         MockitoAnnotations.initMocks(this);
         TransportProtocol protocol1 = getMockTransportProtocol();
         TransportProtocol protocol2 = getMockTransportProtocol();
-
         mTransportProtocols.add(protocol1);
         mTransportProtocols.add(protocol2);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportConversionTest.java b/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportConversionTest.java
index 72fda53..0c251a0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportConversionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportConversionTest.java
@@ -26,7 +26,6 @@
 
 import android.os.Handler;
 import android.telephony.ims.RtpHeaderExtension;
-import android.telephony.ims.RtpHeaderExtensionType;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 
@@ -84,7 +83,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mRtpTransport = new RtpTransport(mRtpAdapter, mTimeoutsAdapter, mHandler);
+        mRtpTransport = new RtpTransport(mRtpAdapter, mTimeoutsAdapter, mHandler, true /* sdp */);
         mRtpTransport.setCallback(mCallback);
 
         when(mRtpAdapter.getAcceptedRtpHeaderExtensions()).thenReturn(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportTest.java b/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportTest.java
index 4208212..c241cf2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportTest.java
@@ -67,7 +67,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mRtpTransport = new RtpTransport(mRtpAdapter, mTimeoutsAdapter, mHandler);
+        mRtpTransport = new RtpTransport(mRtpAdapter, mTimeoutsAdapter, mHandler, true /* sdp */);
         mRtpTransport.setCallback(mCallback);
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
index 1940ad0..a6cdcae 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -419,11 +419,11 @@
         assertEquals("spmode.ne.jp", dpCaptor.getValue().getApn());
         if (tdCaptor.getValue() != null) {
             if (mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
-                assertEquals(null, tdCaptor.getValue().getDnn());
+                assertEquals(null, tdCaptor.getValue().getDataNetworkName());
                 assertTrue(tdCaptor.getValue().getOsAppId()
                         .contains(ApnSetting.TYPE_ENTERPRISE_STRING));
             } else {
-                assertEquals("spmode.ne.jp", tdCaptor.getValue().getDnn());
+                assertEquals("spmode.ne.jp", tdCaptor.getValue().getDataNetworkName());
                 assertEquals(null, tdCaptor.getValue().getOsAppId());
             }
         }
@@ -789,12 +789,14 @@
         assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
         mDc.onMeterednessChanged(true);
+        waitForMs(100);
 
         assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
         assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
         assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
         mDc.onMeterednessChanged(false);
+        waitForMs(100);
 
         assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
         assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
@@ -813,12 +815,14 @@
         assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
         mDc.onCongestednessChanged(true);
+        waitForMs(100);
 
         assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
         assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
         assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
         mDc.onCongestednessChanged(false);
+        waitForMs(100);
 
         assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
         assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
index 55f621a..b641ecd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
@@ -107,6 +107,33 @@
             -1,                     // mvno_type
             "");                    // mnvo_match_data
 
+    private ApnSetting mApn4 = ApnSetting.makeApnSetting(
+            2163,                   // id
+            "44010",                // numeric
+            "sp-mode",              // name
+            "fake_apn",             // apn
+            null,                   // proxy
+            -1,                     // port
+            null,                   // mmsc
+            null,                   // mmsproxy
+            -1,                     // mmsport
+            "user",                 // user
+            "passwd",               // password
+            -1,                     // authtype
+            ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL,   // types
+            ApnSetting.PROTOCOL_IP,                 // protocol
+            ApnSetting.PROTOCOL_IP,                 // roaming_protocol
+            true,                   // carrier_enabled
+            10360,                  // networktype_bitmask
+            1234,                   // profile_id
+            false,                  // modem_cognitive
+            111,                    // max_conns
+            456,                    // wait_time
+            789,                    // max_conns_time
+            0,                      // mtu
+            -1,                     // mvno_type
+            "");                    // mnvo_match_data
+
     @SmallTest
     public void testCreateFromApnSetting() throws Exception {
         DataProfile dp = DcTracker.createDataProfile(mApn1, mApn1.getProfileId(), false);
@@ -132,11 +159,13 @@
         assertEquals(RILConstants.SETUP_DATA_AUTH_PAP_CHAP, dp.getAuthType());
         assertEquals(mApn3.getUser(), dp.getUserName());
         assertEquals(mApn3.getPassword(), dp.getPassword());
-        assertEquals(2, dp.getType());  // TYPE_3GPP2
+        assertEquals(0, dp.getType());  // TYPE_COMMON
         assertEquals(mApn3.getWaitTime(), dp.getWaitTime());
         assertEquals(mApn3.isEnabled(), dp.isEnabled());
         int expectedBearerBitmap = mApn3.getNetworkTypeBitmask();
         assertEquals(expectedBearerBitmap, dp.getBearerBitmask());
+        dp = DcTracker.createDataProfile(mApn4, mApn4.getProfileId(), false);
+        assertEquals(2, dp.getType());  // TYPE_3GPP2
     }
 
     @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index 23e7b04..a1b91e5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -1199,7 +1199,7 @@
                 eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
                 eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
                 anyBoolean(), any(Message.class));
-        assertEquals("FAKE APN 1", tdCaptor.getValue().getDnn());
+        assertEquals("FAKE APN 1", tdCaptor.getValue().getDataNetworkName());
         assertEquals(null, tdCaptor.getValue().getOsAppId());
 
         mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
@@ -1218,8 +1218,8 @@
                 eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
                 eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
                 anyBoolean(), any(Message.class));
-        assertEquals(null, tdCaptor.getValue().getDnn());
-        assertTrue(tdCaptor.getValue().getOsAppId().contains(ApnSetting.TYPE_ENTERPRISE_STRING));
+        assertEquals(null, tdCaptor.getValue().getDataNetworkName());
+        assertTrue(tdCaptor.getValue().getOsAppId().equals("ENTERPRISE"));
     }
 
     @Test
@@ -2186,7 +2186,7 @@
 
         // NetCapability should switch to unmetered with fr=MMWAVE
         doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE))
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED))
                 .when(mDisplayInfoController).getTelephonyDisplayInfo();
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
         waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
@@ -2223,7 +2223,7 @@
 
         // NetCapability should switch to unmetered when fr=MMWAVE and MMWAVE unmetered
         doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE))
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED))
                 .when(mDisplayInfoController).getTelephonyDisplayInfo();
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
         waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/LinkBandwidthEstimatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/LinkBandwidthEstimatorTest.java
new file mode 100644
index 0000000..e48f183
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/LinkBandwidthEstimatorTest.java
@@ -0,0 +1,547 @@
+/*
+ * 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.dataconnection;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.BW_STATS_COUNT_THRESHOLD;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.LINK_RX;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.LINK_TX;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_DEFAULT_NETWORK_CHANGED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_MODEM_ACTIVITY_RETURNED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_NR_FREQUENCY_CHANGED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_SCREEN_STATE_CHANGED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_SIGNAL_STRENGTH_CHANGED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.UNKNOWN_TAC;
+
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.NetworkCapabilities;
+import android.telephony.CellIdentityLte;
+import android.telephony.ModemActivityInfo;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Pair;
+
+import com.android.internal.telephony.TelephonyFacade;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class LinkBandwidthEstimatorTest extends TelephonyTest {
+    private LinkBandwidthEstimator mLBE;
+    private static final int [] TX_TIME_1_MS = new int[]{0, 0, 0, 0, 0};
+    private static final int [] TX_TIME_2_MS = new int[]{100, 0, 0, 0, 100};
+    private static final int RX_TIME_1_MS = 100;
+    private static final int RX_TIME_2_MS = 200;
+    private static final ModemActivityInfo MAI_INIT =
+            new ModemActivityInfo(0, 0, 0, TX_TIME_1_MS, RX_TIME_1_MS);
+    private static final ModemActivityInfo MAI_TX_RX_TIME_HIGH =
+            new ModemActivityInfo(100L, 0, 0, TX_TIME_2_MS, RX_TIME_2_MS);
+    private static final ModemActivityInfo MAI_RX_TIME_HIGH =
+            new ModemActivityInfo(100L, 0, 0, TX_TIME_1_MS, RX_TIME_2_MS);
+    private NetworkCapabilities mNetworkCapabilities;
+    private CellIdentityLte mCellIdentity;
+    private long mElapsedTimeMs = 0;
+    private long mTxBytes = 0;
+    private long mRxBytes = 0;
+    @Mock
+    TelephonyFacade mTelephonyFacade;
+    @Mock
+    DataConnection mDataConnection;
+    private NetworkRegistrationInfo mNri;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mNetworkCapabilities = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .build();
+
+        mCellIdentity = new CellIdentityLte(310, 260, 1234, 123456, 366);
+        mNri = new NetworkRegistrationInfo.Builder()
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())).thenReturn(mNri);
+        when(mTelephonyFacade.getElapsedSinceBootMillis()).thenReturn(0L);
+        when(mTelephonyFacade.getMobileTxBytes()).thenReturn(0L);
+        when(mTelephonyFacade.getMobileTxBytes()).thenReturn(0L);
+        when(mPhone.getCurrentCellIdentity()).thenReturn(mCellIdentity);
+        when(mDcTracker.getDataConnectionByApnType(anyString())).thenReturn(mDataConnection);
+        when(mSignalStrength.getDbm()).thenReturn(-100);
+        when(mSignalStrength.getLevel()).thenReturn(1);
+        mLBE = new LinkBandwidthEstimator(mPhone, mTelephonyFacade);
+        mLBE.obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, mNetworkCapabilities).sendToTarget();
+        processAllMessages();
+    }
+
+    private void addElapsedTime(long timeMs) {
+        mElapsedTimeMs += timeMs;
+        when(mTelephonyFacade.getElapsedSinceBootMillis()).thenReturn(mElapsedTimeMs);
+    }
+
+    private void addTxBytes(long txBytes) {
+        mTxBytes += txBytes;
+        when(mTelephonyFacade.getMobileTxBytes()).thenReturn(mTxBytes);
+    }
+
+    private void addRxBytes(long rxBytes) {
+        mRxBytes += rxBytes;
+        when(mTelephonyFacade.getMobileRxBytes()).thenReturn(mRxBytes);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testScreenOnTxTrafficHighOneModemPoll() throws Exception {
+        addElapsedTime(4_100);
+        moveTimeForward(4_100);
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        addTxBytes(500_000L);
+        addRxBytes(10_000L);
+        addElapsedTime(2_100);
+        moveTimeForward(2_100);
+        processAllMessages();
+
+        verify(mTelephonyManager, times(1)).requestModemActivityInfo(any(), any());
+    }
+
+    @Test
+    public void testScreenOnTxRxTrafficHighTwoModemPoll() throws Exception {
+        addElapsedTime(4_100);
+        moveTimeForward(4_100);
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        processAllMessages();
+
+        addTxBytes(10_000L);
+        addRxBytes(20_000L);
+        addElapsedTime(2_100);
+        moveTimeForward(2_100);
+        processAllMessages();
+        verify(mTelephonyManager, times(1)).requestModemActivityInfo(any(), any());
+
+        mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, MAI_INIT).sendToTarget();
+        processAllMessages();
+
+        addTxBytes(100_000L);
+        addRxBytes(200_000L);
+        addElapsedTime(5_100);
+        moveTimeForward(5_100);
+        processAllMessages();
+
+        mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, MAI_TX_RX_TIME_HIGH).sendToTarget();
+        processAllMessages();
+
+        verify(mTelephonyManager, times(2)).requestModemActivityInfo(any(), any());
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+    }
+
+    @Test
+    public void testScreenOnRxTrafficHighTwoModemPollRxTimeHigh() throws Exception {
+        addElapsedTime(4_100);
+        moveTimeForward(4_100);
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        processAllMessages();
+
+        addTxBytes(10_000L);
+        addRxBytes(20_000L);
+        addElapsedTime(2_100);
+        moveTimeForward(2_100);
+        processAllMessages();
+        verify(mTelephonyManager, times(1)).requestModemActivityInfo(any(), any());
+
+        mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, MAI_INIT).sendToTarget();
+        processAllMessages();
+
+        addTxBytes(100_000L);
+        addRxBytes(200_000L);
+        addElapsedTime(5_100);
+        moveTimeForward(5_100);
+        processAllMessages();
+
+        mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, MAI_RX_TIME_HIGH).sendToTarget();
+        processAllMessages();
+
+        verify(mTelephonyManager, times(2)).requestModemActivityInfo(any(), any());
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+    }
+
+    @Test
+    public void testScreenOnTxRxTrafficLow() throws Exception {
+        addElapsedTime(4_100);
+        moveTimeForward(4_100);
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        addTxBytes(10_000L);
+        addRxBytes(10_000L);
+        addElapsedTime(2_100);
+        moveTimeForward(2_100);
+        processAllMessages();
+        verify(mTelephonyManager, never()).requestModemActivityInfo(any(), any());
+    }
+
+    @Test
+    public void testScreenOnTrafficLowSampleHighAcc() throws Exception {
+        addElapsedTime(4_100);
+        moveTimeForward(4_100);
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        for (int i = 0; i < 30; i++) {
+            addTxBytes(10_000L);
+            addRxBytes(19_000L);
+            addElapsedTime(1_100);
+            moveTimeForward(1_100);
+            processAllMessages();
+        }
+        verify(mTelephonyManager, times(2)).requestModemActivityInfo(any(), any());
+    }
+
+    @Test
+    public void testScreenOnDefaultNetworkToggleNoExtraTrafficPoll() throws Exception {
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        processAllMessages();
+        mLBE.obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, null).sendToTarget();
+        addElapsedTime(500);
+        moveTimeForward(500);
+        processAllMessages();
+        mLBE.obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, mNetworkCapabilities).sendToTarget();
+        for (int i = 0; i < 3; i++) {
+            addElapsedTime(1_100);
+            moveTimeForward(1_100);
+            processAllMessages();
+        }
+
+        verify(mTelephonyFacade, times(4)).getMobileTxBytes();
+    }
+
+    @Test
+    public void testRatChangeTriggerBandwidthUpdate() throws Exception {
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        addTxBytes(10_000L);
+        addRxBytes(19_000L);
+        addElapsedTime(2000);
+        moveTimeForward(2000);
+        processAllMessages();
+
+        addTxBytes(10_000L);
+        addRxBytes(19_000L);
+        mNri = new NetworkRegistrationInfo.Builder()
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())).thenReturn(mNri);
+        addElapsedTime(6000);
+        moveTimeForward(6000);
+        processAllMessages();
+
+        verify(mTelephonyManager, times(0)).requestModemActivityInfo(any(), any());
+        verify(mDataConnection, times(2)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+    }
+
+    @Test
+    public void testSignalLevelChangeTriggerBandwidthUpdate() throws Exception {
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        processAllMessages();
+
+        for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 2; i++) {
+            addTxBytes(10_000L);
+            addRxBytes(500_000L);
+            addElapsedTime(5_100);
+            moveTimeForward(5_100);
+            processAllMessages();
+            mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+                    i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+            processAllMessages();
+        }
+
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(19_597));
+
+        addTxBytes(20_000L);
+        addRxBytes(50_000L);
+        when(mSignalStrength.getDbm()).thenReturn(-110);
+        mLBE.obtainMessage(MSG_SIGNAL_STRENGTH_CHANGED, mSignalStrength).sendToTarget();
+        addElapsedTime(6000);
+        moveTimeForward(6000);
+        processAllMessages();
+
+        verify(mDataConnection, times(2)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+    }
+
+    @Test
+    public void testAvgBwForAllPossibleRat() throws Exception {
+        Pair<Integer, Integer> values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_GPRS);
+        assertEquals(24, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_EDGE);
+        assertEquals(18, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_UMTS);
+        assertEquals(115, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_CDMA);
+        assertEquals(14, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_1xRTT);
+        assertEquals(30, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_EVDO_0);
+        assertEquals(48, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_EVDO_A);
+        assertEquals(550, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_HSDPA);
+        assertEquals(620, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_HSUPA);
+        assertEquals(1800, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_HSPA);
+        assertEquals(1800, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_EVDO_B);
+        assertEquals(550, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_EHRPD);
+        assertEquals(750, (int) values.first);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_HSPAP);
+        assertEquals(3400, (int) values.second);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_TD_SCDMA);
+        assertEquals(115, (int) values.first);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_LTE);
+        assertEquals(15000, (int) values.second);
+        when(mServiceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+        when(mServiceState.getNrFrequencyRange()).thenReturn(ServiceState.FREQUENCY_RANGE_MMWAVE);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_LTE);
+        assertEquals(145000, (int) values.first);
+        when(mServiceState.getNrFrequencyRange()).thenReturn(ServiceState.FREQUENCY_RANGE_UNKNOWN);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_LTE);
+        assertEquals(47000, (int) values.first);
+        values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_NR);
+        assertEquals(145_000, (int) values.first);
+    }
+
+    @Test
+    public void testSwitchToNrMmwaveTriggerBandwidthUpdate() throws Exception {
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        addTxBytes(10_000L);
+        addRxBytes(19_000L);
+        addElapsedTime(2000);
+        moveTimeForward(2000);
+        processAllMessages();
+
+        addTxBytes(10_000L);
+        addRxBytes(19_000L);
+        when(mServiceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+        when(mServiceState.getNrFrequencyRange()).thenReturn(ServiceState.FREQUENCY_RANGE_MMWAVE);
+        when(mSignalStrength.getLevel()).thenReturn(2);
+        mLBE.obtainMessage(MSG_NR_FREQUENCY_CHANGED).sendToTarget();
+        addElapsedTime(6000);
+        moveTimeForward(6000);
+        processAllMessages();
+
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+    }
+
+    @Test
+    public void testEnoughModemPollTriggerBwUpdate() throws Exception {
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        processAllMessages();
+
+        for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 2; i++) {
+            addTxBytes(10_000L);
+            addRxBytes(500_000L);
+            addElapsedTime(5_100);
+            moveTimeForward(5_100);
+            processAllMessages();
+            mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+                    i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+            processAllMessages();
+        }
+
+        verify(mTelephonyManager, times(BW_STATS_COUNT_THRESHOLD + 2))
+                .requestModemActivityInfo(any(), any());
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(19_597));
+    }
+
+    @Test
+    public void testUseCurrentTacStatsWithEnoughData() throws Exception {
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        processAllMessages();
+
+        for (int i = 0; i < BW_STATS_COUNT_THRESHOLD; i++) {
+            addTxBytes(10_000L);
+            addRxBytes(500_000L);
+            addElapsedTime(5_100);
+            moveTimeForward(5_100);
+            processAllMessages();
+            mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+                    i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+            processAllMessages();
+        }
+
+        mCellIdentity = new CellIdentityLte(310, 260, 1235, 123457, 367);
+        when(mPhone.getCurrentCellIdentity()).thenReturn(mCellIdentity);
+        for (int i = BW_STATS_COUNT_THRESHOLD; i < 3 * BW_STATS_COUNT_THRESHOLD; i++) {
+            addTxBytes(10_000L);
+            addRxBytes(500_000L);
+            addElapsedTime(5_100);
+            moveTimeForward(5_100);
+            processAllMessages();
+            mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+                    i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+            processAllMessages();
+        }
+
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(19_597));
+    }
+
+    @Test
+    public void testUseAllTacStatsIfNoEnoughDataWithCurrentTac() throws Exception {
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        processAllMessages();
+        mLBE.obtainMessage(MSG_SIGNAL_STRENGTH_CHANGED, mSignalStrength).sendToTarget();
+        processAllMessages();
+
+        for (int i = 0; i < BW_STATS_COUNT_THRESHOLD; i++) {
+            addTxBytes(10_000L);
+            addRxBytes(900_000L);
+            addElapsedTime(5_100);
+            moveTimeForward(5_100);
+            processAllMessages();
+            mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+                    i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+            processAllMessages();
+        }
+
+        mCellIdentity = new CellIdentityLte(310, 260, 1234, 123456, 367);
+        when(mPhone.getCurrentCellIdentity()).thenReturn(mCellIdentity);
+        for (int i = BW_STATS_COUNT_THRESHOLD; i < BW_STATS_COUNT_THRESHOLD * 3 / 2; i++) {
+            addTxBytes(10_000L);
+            addRxBytes(1_000_000L);
+            addElapsedTime(5_100);
+            moveTimeForward(5_100);
+            processAllMessages();
+            mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+                    i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+            processAllMessages();
+        }
+
+        LinkBandwidthEstimator.NetworkBandwidth network = mLBE.lookupNetwork("310260", 366, "LTE");
+        assertEquals(BW_STATS_COUNT_THRESHOLD - 1, network.getCount(LINK_RX, 1));
+        assertEquals(900_000L * 8 * 1000 / 200 / 1024 * (BW_STATS_COUNT_THRESHOLD - 1),
+                network.getValue(LINK_RX, 1));
+        network = mLBE.lookupNetwork("310260", 367, "LTE");
+        assertEquals(2, network.getCount(LINK_RX, 1));
+        assertEquals(1_000_000L * 8 * 1000 / 200 / 1024 * 2,
+                network.getValue(LINK_RX, 1));
+        network = mLBE.lookupNetwork("310260", UNKNOWN_TAC, "LTE");
+        assertEquals(BW_STATS_COUNT_THRESHOLD * 3 / 2 - 1, network.getCount(LINK_RX, 1));
+        assertEquals(218_748, network.getValue(LINK_RX, 1));
+        verify(mDataConnection, times(2)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+    }
+
+    @Test
+    public void testSwitchCarrierFallbackToColdStartValue() throws Exception {
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        processAllMessages();
+
+        for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 5; i++) {
+            addTxBytes(10_000L);
+            addRxBytes(500_000L);
+            addElapsedTime(5_100);
+            moveTimeForward(5_100);
+            processAllMessages();
+            mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+                    i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+            processAllMessages();
+        }
+
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(19_597));
+
+        mCellIdentity = new CellIdentityLte(320, 265, 1234, 123456, 366);
+        when(mPhone.getCurrentCellIdentity()).thenReturn(mCellIdentity);
+
+        addTxBytes(10_000L);
+        addRxBytes(10_000L);
+        addElapsedTime(5_100);
+        moveTimeForward(5_100);
+        processAllMessages();
+
+        verify(mDataConnection, times(2)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+    }
+
+    @Test
+    public void testIgnoreLowTxRxTime() throws Exception {
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        processAllMessages();
+
+        for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 5; i++) {
+            addTxBytes(10_000L);
+            addRxBytes(500_000L);
+            addElapsedTime(5_100);
+            moveTimeForward(5_100);
+            processAllMessages();
+            mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+                    i * 5_100L, 0, 0, TX_TIME_2_MS, i * 80)).sendToTarget();
+            processAllMessages();
+        }
+
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(-1));
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(anyInt(), anyInt());
+    }
+
+    @Test
+    public void testUseHighTxRxByteEdge() throws Exception {
+        mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+        processAllMessages();
+        mNri = new NetworkRegistrationInfo.Builder()
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_EDGE)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())).thenReturn(mNri);
+        mLBE.obtainMessage(MSG_SIGNAL_STRENGTH_CHANGED, mSignalStrength).sendToTarget();
+        processAllMessages();
+        for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 5; i++) {
+            addTxBytes(12_000L);
+            addRxBytes(12_000L);
+            addElapsedTime(5_100);
+            moveTimeForward(5_100);
+            processAllMessages();
+            mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+                    i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS * 5)).sendToTarget();
+            processAllMessages();
+        }
+
+        LinkBandwidthEstimator.NetworkBandwidth network = mLBE.lookupNetwork("310260", 366, "EDGE");
+
+        assertEquals(0, network.getCount(LINK_TX, 1));
+        assertEquals(BW_STATS_COUNT_THRESHOLD + 4, network.getCount(LINK_RX, 1));
+        assertEquals(12_000L * 8 / 1024 * (BW_STATS_COUNT_THRESHOLD + 4),
+                network.getValue(LINK_RX, 1));
+        verify(mDataConnection, times(1)).updateLinkBandwidthEstimation(eq(-1), eq(92));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
index 7c33f34..43de6f6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -1290,7 +1290,8 @@
     private void callGetDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
             boolean complete, GetDownloadableSubscriptionMetadataResult result) {
         prepareGetDownloadableSubscriptionMetadataCall(complete, result);
-        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         mController.getDownloadableSubscriptionMetadata(0, subscription, PACKAGE_NAME,
                 resultCallback);
     }
@@ -1311,14 +1312,16 @@
             }
         }).when(mMockConnector).getDefaultDownloadableSubscriptionList(anyInt(), anyBoolean(),
                 any());
-        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         mController.getDefaultDownloadableSubscriptionList(CARD_ID, PACKAGE_NAME, resultCallback);
     }
 
     private void callDownloadSubscription(DownloadableSubscription subscription,
             boolean switchAfterDownload, final boolean complete, final int result,
             final int resolvableError, String callingPackage) {
-        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         doAnswer(new Answer<Void>() {
             @Override
             public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1345,7 +1348,8 @@
 
     private void callDeleteSubscription(int subscriptionId, String iccid, final boolean complete,
             final int result, String callingPackage) {
-        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         doAnswer(new Answer<Void>() {
             @Override
             public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1364,7 +1368,8 @@
 
     private void callSwitchToSubscription(int subscriptionId, String iccid, final boolean complete,
             final int result, String callingPackage) {
-        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         doAnswer(new Answer<Void>() {
             @Override
             public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1383,7 +1388,8 @@
 
     private void callUpdateSubscriptionNickname(int subscriptionId, String iccid, String nickname,
             final boolean complete, final int result, String callingPackage) {
-        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         doAnswer(new Answer<Void>() {
             @Override
             public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1403,7 +1409,8 @@
     }
 
     private void callEraseSubscriptions(final boolean complete, final int result) {
-        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         doAnswer(new Answer<Void>() {
             @Override
             public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1421,7 +1428,8 @@
     }
 
     private void callEraseSubscriptionsWithOptions(final boolean complete, final int result) {
-        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         doAnswer(new Answer<Void>() {
             @Override
             public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1440,7 +1448,8 @@
     }
 
     private void callRetainSubscriptionsForFactoryReset(final boolean complete, final int result) {
-        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         doAnswer(new Answer<Void>() {
             @Override
             public Void answer(InvocationOnMock invocation) throws Exception {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index d201bcf..4796a97 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -37,6 +37,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Notification;
+import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -381,6 +383,61 @@
 
     @Test
     @MediumTest
+    public void testNewSmsWithUserLocked_notificationShown() {
+        // user locked
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        doReturn(false).when(userManager).isUserUnlocked();
+
+        transitionFromStartupToIdle();
+
+        sendNewSms();
+
+        verify(mContext, never()).sendBroadcast(any(Intent.class));
+        assertEquals("IdleState", getCurrentState().getName());
+
+        // Filter should be invoked.
+        verifySmsFiltersInvoked(times(1));
+
+        // New message notification should be shown.
+        NotificationManager notificationManager =
+                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        verify(notificationManager).notify(
+                eq(InboundSmsHandler.NOTIFICATION_TAG),
+                eq(InboundSmsHandler.NOTIFICATION_ID_NEW_MESSAGE),
+                any(Notification.class));
+    }
+
+    @Test
+    @MediumTest
+    public void testNewSmsFromBlockedNumberWithUserLocked_noNotificationShown() {
+        String blockedNumber = "1234567890";
+        mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber);
+
+        // user locked
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        doReturn(false).when(userManager).isUserUnlocked();
+
+        transitionFromStartupToIdle();
+
+        sendNewSms();
+
+        verify(mContext, never()).sendBroadcast(any(Intent.class));
+        assertEquals("IdleState", getCurrentState().getName());
+
+        // Filter should be invoked.
+        verifySmsFiltersInvoked(times(1));
+
+        // No new message notification should be shown.
+        NotificationManager notificationManager =
+                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        verify(notificationManager, never()).notify(
+                eq(InboundSmsHandler.NOTIFICATION_TAG),
+                eq(InboundSmsHandler.NOTIFICATION_ID_NEW_MESSAGE),
+                any(Notification.class));
+    }
+
+    @Test
+    @MediumTest
     public void testNewSms_filterInvoked_noBroadcastsSent() {
         // Configure the first filter to drop the SMS.
         when(mSmsFilter.filterSms(any(byte[][].class), anyInt(),
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 405e237..c69c875 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -281,7 +281,7 @@
 
         ArrayList<PendingIntent> sentIntents = new ArrayList<>();
         PendingIntent sentIntent = PendingIntent.getBroadcast(realContext, 0,
-                new Intent(TEST_INTENT), 0);
+                new Intent(TEST_INTENT), PendingIntent.FLAG_IMMUTABLE);
         sentIntents.add(sentIntent);
         sentIntents.add(sentIntent);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 2b7e0cf..aacfd4a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -1281,13 +1281,23 @@
     }
 
     /**
-     * Ensures when D2D communication is supported that we register the expected D2D RTP header
-     * extension types.
+     * Ensures when both RTP and SDP is supported that we register the expected header extension
+     * types.
      * @throws Exception
      */
     @Test
     @SmallTest
     public void testConfigureRtpHeaderExtensionTypes() throws Exception {
+
+        mContextFixture.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
+                true);
+        mContextFixture.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL,
+                true);
+        // Hacky but ImsPhoneCallTracker caches carrier config, so necessary.
+        mCTUT.updateCarrierConfigCache(mContextFixture.getCarrierConfigBundle());
+
         ImsPhoneCallTracker.Config config = new ImsPhoneCallTracker.Config();
         config.isD2DCommunicationSupported = true;
         mCTUT.setConfig(config);
@@ -1303,6 +1313,35 @@
     }
 
     /**
+     * Ensures when SDP is not supported (by RTP is) we don't register any extensions.
+     * @throws Exception
+     */
+    @Test
+    @SmallTest
+    public void testRtpButNoSdp() throws Exception {
+
+        mContextFixture.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
+                true);
+        mContextFixture.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL,
+                false);
+        // Hacky but ImsPhoneCallTracker caches carrier config, so necessary.
+        mCTUT.updateCarrierConfigCache(mContextFixture.getCarrierConfigBundle());
+
+        ImsPhoneCallTracker.Config config = new ImsPhoneCallTracker.Config();
+        config.isD2DCommunicationSupported = true;
+        mCTUT.setConfig(config);
+        mConnectorListener.connectionReady(mImsManager);
+
+        // Expect to get offered header extensions since d2d is supported.
+        verify(mImsManager).setOfferedRtpHeaderExtensionTypes(
+                mRtpHeaderExtensionTypeCaptor.capture());
+        Set<RtpHeaderExtensionType> types = mRtpHeaderExtensionTypeCaptor.getValue();
+        assertEquals(0, types.size());
+    }
+
+    /**
      * Ensures when D2D communication is not supported that we don't register the D2D RTP header
      * extension types.
      * @throws Exception