Merge "Fixed the set/getAllowedNetworkTypes debug messages"
diff --git a/assets/CarrierRestrictionOperatorDetails.json b/assets/CarrierRestrictionOperatorDetails.json
new file mode 100644
index 0000000..166cc39
--- /dev/null
+++ b/assets/CarrierRestrictionOperatorDetails.json
@@ -0,0 +1,4 @@
+{
+  "_comment": "Operator should register with its application package name, carrierId and all the corresponding  SHAIDs",
+  "_comment": "Example format :: << \"packageName\" : {\"carrierId\":<int>, \"callerSHA1Id\":[<SHAID1>, <SHAID2>]} >>"
+}
\ No newline at end of file
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 4d467ab..61f38d8 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -31,6 +31,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.app.PropertyInvalidatedCache;
@@ -436,6 +437,8 @@
 
     private static final int SET_NETWORK_SELECTION_MODE_AUTOMATIC_TIMEOUT_MS = 2000; // 2 seconds
 
+    private static final int MODEM_ACTIVITY_TIME_OFFSET_CORRECTION_MS = 50;
+
     /**
      * With support for MEP(multiple enabled profile) in Android T, a SIM card can have more than
      * one ICCID active at the same time.
@@ -489,13 +492,11 @@
 
     private static final class PurchasePremiumCapabilityArgument {
         public @TelephonyManager.PremiumCapability int capability;
-        public @NonNull String appName;
         public @NonNull IIntegerConsumer callback;
 
         PurchasePremiumCapabilityArgument(@TelephonyManager.PremiumCapability int capability,
-                @NonNull String appName, @NonNull IIntegerConsumer callback) {
+                @NonNull IIntegerConsumer callback) {
             this.capability = capability;
-            this.appName = appName;
             this.callback = callback;
         }
     }
@@ -1467,6 +1468,8 @@
                         ModemActivityInfo info = (ModemActivityInfo) ar.result;
                         if (isModemActivityInfoValid(info)) {
                             mergeModemActivityInfo(info);
+                        } else {
+                            loge("queryModemActivityInfo: invalid response");
                         }
                         // This is needed to decouple ret from mLastModemActivityInfo
                         // We don't want to return mLastModemActivityInfo which is updated
@@ -2177,7 +2180,7 @@
                     PurchasePremiumCapabilityArgument arg =
                             (PurchasePremiumCapabilityArgument) request.argument;
                     SlicePurchaseController.getInstance(request.phone).purchasePremiumCapability(
-                            arg.capability, arg.appName, onCompleted);
+                            arg.capability, onCompleted);
                     break;
                 }
 
@@ -2422,8 +2425,7 @@
         mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance();
         mNotifyUserActivity = new AtomicBoolean(false);
         PropertyInvalidatedCache.invalidateCache(TelephonyManager.CACHE_KEY_PHONE_ACCOUNT_TO_SUBID);
-        mTelephony2gUpdater = new Telephony2gUpdater(
-                Executors.newSingleThreadExecutor(), mApp);
+        mTelephony2gUpdater = new Telephony2gUpdater(mApp);
         mTelephony2gUpdater.init();
         publish();
     }
@@ -8028,7 +8030,7 @@
         }
     }
 
-    // Checks that ModemActivityInfo is valid. Sleep time, Idle time, Rx time and Tx time should be
+    // Checks that ModemActivityInfo is valid. Sleep time and Idle time should be
     // less than total activity duration.
     private boolean isModemActivityInfoValid(ModemActivityInfo info) {
         if (info == null) {
@@ -8036,13 +8038,13 @@
         }
         int activityDurationMs =
                 (int) (info.getTimestampMillis() - mLastModemActivityInfo.getTimestampMillis());
+        activityDurationMs += MODEM_ACTIVITY_TIME_OFFSET_CORRECTION_MS;
+
         int totalTxTimeMs = Arrays.stream(info.getTransmitTimeMillis()).sum();
 
         return (info.isValid()
             && (info.getSleepTimeMillis() <= activityDurationMs)
-            && (info.getIdleTimeMillis() <= activityDurationMs)
-            && (info.getReceiveTimeMillis() <= activityDurationMs)
-            && (totalTxTimeMs <= activityDurationMs));
+            && (info.getIdleTimeMillis() <= activityDurationMs));
     }
 
     private void updateLastModemActivityInfo(ModemActivityInfo info, int rat, int freq) {
@@ -11452,6 +11454,12 @@
                     + " failed due to missing permissions.");
             throw new SecurityException("purchasePremiumCapability requires permission "
                     + "READ_BASIC_PHONE_STATE.");
+        } else if (!TelephonyPermissions.checkInternetPermissionNoThrow(
+                mApp, "purchasePremiumCapability")) {
+            log("purchasePremiumCapability "
+                    + TelephonyManager.convertPremiumCapabilityToString(capability)
+                    + " failed due to missing permissions.");
+            throw new SecurityException("purchasePremiumCapability requires permission INTERNET.");
         }
 
         Phone phone = getPhone(subId);
@@ -11470,15 +11478,50 @@
             }
             return;
         }
-        String appName;
+
+        String callingProcess;
         try {
-            appName = mApp.getPackageManager().getApplicationLabel(mApp.getPackageManager()
-                    .getApplicationInfo(getCurrentPackageName(), 0)).toString();
+            callingProcess = mApp.getPackageManager().getApplicationInfo(
+                    getCurrentPackageName(), 0).processName;
         } catch (PackageManager.NameNotFoundException e) {
-            appName = "An application";
+            callingProcess = getCurrentPackageName();
         }
+
+        boolean isVisible = false;
+        ActivityManager am = mApp.getSystemService(ActivityManager.class);
+        if (am != null) {
+            List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
+            if (processes != null) {
+                for (ActivityManager.RunningAppProcessInfo process : processes) {
+                    log("purchasePremiumCapability: process " + process.processName
+                            + "has importance " + process.importance);
+                    if (process.processName.equals(callingProcess) && process.importance
+                            <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
+                        isVisible = true;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (!isVisible) {
+            try {
+                int result = TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND;
+                callback.accept(result);
+                loge("purchasePremiumCapability: " + callingProcess + " is not in the foreground.");
+            } catch (RemoteException e) {
+                String logStr = "Purchase premium capability "
+                        + TelephonyManager.convertPremiumCapabilityToString(capability)
+                        + " failed due to RemoteException handling background application: " + e;
+                if (DBG) log(logStr);
+                AnomalyReporter.reportAnomaly(
+                        UUID.fromString(PURCHASE_PREMIUM_CAPABILITY_ERROR_UUID), logStr);
+            }
+            return;
+        }
+
         sendRequestAsync(CMD_PURCHASE_PREMIUM_CAPABILITY,
-                new PurchasePremiumCapabilityArgument(capability, appName, callback), phone, null);
+                new PurchasePremiumCapabilityArgument(capability, callback), phone, null);
     }
 
     /**
diff --git a/src/com/android/phone/Telephony2gUpdater.java b/src/com/android/phone/Telephony2gUpdater.java
index 0919385..baaa684 100644
--- a/src/com/android/phone/Telephony2gUpdater.java
+++ b/src/com/android/phone/Telephony2gUpdater.java
@@ -30,8 +30,13 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.RILConstants;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 
 /**
  * A {@link BroadcastReceiver} that ensures that user restrictions are correctly applied to
@@ -39,27 +44,50 @@
  * This includes handling broadcasts from user restriction state changes, as well as ensuring that
  * SIM-specific settings are correctly applied when new subscriptions become active.
  *
+ * <p>
  * Callers are expected to call {@code init()} and keep an instance of this class alive.
+ * </p>
  */
 public class Telephony2gUpdater extends BroadcastReceiver {
-    private static final String TAG = "TelephonyUserManagerReceiver";
+    private static final String TAG = "Telephony2gUpdater";
 
     // We can't interact with the HAL on the main thread of the phone process (where
     // receivers are run by default), so we execute our logic from a separate thread.
+    // The correctness of this implementation relies heavily on this executor ensuring
+    // tasks are serially executed i.e. ExecutorService.newSingleThreadExecutor()
     private final Executor mExecutor;
     private final Context mContext;
     private final long mBaseAllowedNetworks;
 
-    public Telephony2gUpdater(Executor executor, Context context) {
-        this(executor, context,
+    private UserManager mUserManager;
+    private TelephonyManager mTelephonyManager;
+    private SubscriptionManager mSubscriptionManager;
+
+    // The current subscription ids
+    // Ensure this value is never accessed concurrently
+    private Set<Integer> mCurrentSubscriptions;
+    // We keep track of the last value to avoid updating when unrelated user restrictions change
+    // Ensure this value is never accessed concurrently
+    private boolean mDisallowCellular2gRestriction;
+
+    public Telephony2gUpdater(Context context) {
+        this(Executors.newSingleThreadExecutor(), context,
                 RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE));
     }
 
-    public Telephony2gUpdater(Executor executor, Context context,
-            long baseAllowedNetworks) {
+    @VisibleForTesting
+    public Telephony2gUpdater(Executor executor, Context context, long baseAllowedNetworks) {
         mExecutor = executor;
         mContext = context;
         mBaseAllowedNetworks = baseAllowedNetworks;
+
+        mUserManager = mContext.getSystemService(UserManager.class);
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+
+        // All user restrictions are false by default
+        mDisallowCellular2gRestriction = false;
+        mCurrentSubscriptions = new HashSet<>();
     }
 
     /**
@@ -80,41 +108,42 @@
         Log.i(TAG, "Received callback for action " + intent.getAction());
         final PendingResult result = goAsync();
         mExecutor.execute(() -> {
-            Log.i(TAG, "Running handler for action " + intent.getAction());
-            handleUserRestrictionsChanged(context);
-            result.finish();
+            boolean disallow2g = mUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G);
+            if (mDisallowCellular2gRestriction == disallow2g) {
+                Log.i(TAG, "No update to DISALLOW_CELLULAR_2G restriction.");
+                return;
+            }
+
+            mDisallowCellular2gRestriction = disallow2g;
+
+            Log.i(TAG, "Running handler for all subscriptions based on DISALLOW_CELLULAR_2G change."
+                    + " Restriction value: " + mDisallowCellular2gRestriction);
+            handleUserRestrictionsChanged(mCurrentSubscriptions);
+            if (result != null) {
+                result.finish();
+            }
         });
     }
 
     /**
-     * Update all active subscriptions with allowed network types depending on the current state
-     * of the {@link UserManager.DISALLOW_2G}.
+     * Update subscriptions with allowed network types depending on the current state
+     * of the {@link UserManager#DISALLOW_CELLULAR_2G}.
+     *
+     * @param subIds A list of subIds to update.
      */
-    @VisibleForTesting
-    public void handleUserRestrictionsChanged(Context context) {
-        UserManager um = context.getSystemService(UserManager.class);
-        TelephonyManager tm = context.getSystemService(TelephonyManager.class);
-        SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
+    private void handleUserRestrictionsChanged(Collection<Integer> subIds) {
         final long twoGBitmask = TelephonyManager.NETWORK_CLASS_BITMASK_2G;
 
-        boolean shouldDisable2g = um.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G);
-
-        // This is expected when subscription info cannot be determined. We'll get another
-        // callback in the future from our SubscriptionListener once we have valid subscriptions.
-        List<SubscriptionInfo> subscriptionInfoList = sm.getAvailableSubscriptionInfoList();
-        if (subscriptionInfoList == null) {
-            return;
-        }
-
         long allowedNetworkTypes = mBaseAllowedNetworks;
 
         // 2G device admin controls are global
-        for (SubscriptionInfo info : subscriptionInfoList) {
-            TelephonyManager telephonyManager = tm.createForSubscriptionId(
-                    info.getSubscriptionId());
-            if (shouldDisable2g) {
+        for (Integer subId : subIds) {
+            TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(subId);
+            if (mDisallowCellular2gRestriction) {
+                Log.i(TAG, "Disabling 2g based on user restriction for subId: " + subId);
                 allowedNetworkTypes &= ~twoGBitmask;
             } else {
+                Log.i(TAG, "Enabling 2g based on user restriction for subId: " + subId);
                 allowedNetworkTypes |= twoGBitmask;
             }
             telephonyManager.setAllowedNetworkTypesForReason(
@@ -126,8 +155,30 @@
     private class SubscriptionListener extends SubscriptionManager.OnSubscriptionsChangedListener {
         @Override
         public void onSubscriptionsChanged() {
-            Log.i(TAG, "Running handler for subscription change.");
-            handleUserRestrictionsChanged(mContext);
+            // Note that this entire callback gets invoked in the single threaded executor
+            List<SubscriptionInfo> allSubscriptions =
+                    mSubscriptionManager.getCompleteActiveSubscriptionInfoList();
+
+            HashSet<Integer> updatedSubIds = new HashSet<>(allSubscriptions.size());
+            List<Integer> newSubIds = new ArrayList<>();
+
+            for (SubscriptionInfo info : allSubscriptions) {
+                updatedSubIds.add(info.getSubscriptionId());
+                if (!mCurrentSubscriptions.contains(info.getSubscriptionId())) {
+                    newSubIds.add(info.getSubscriptionId());
+                }
+            }
+
+            mCurrentSubscriptions = updatedSubIds;
+
+            if (newSubIds.isEmpty()) {
+                Log.d(TAG, "No new subIds. Skipping update.");
+                return;
+            }
+
+            Log.i(TAG, "New subscriptions found. Running handler to update 2g restrictions with "
+                    + "subIds " + newSubIds.toString());
+            handleUserRestrictionsChanged(newSubIds);
         }
     }
 
diff --git a/src/com/android/phone/slice/SlicePurchaseController.java b/src/com/android/phone/slice/SlicePurchaseController.java
index 3864119..e36325e 100644
--- a/src/com/android/phone/slice/SlicePurchaseController.java
+++ b/src/com/android/phone/slice/SlicePurchaseController.java
@@ -40,6 +40,7 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.provider.DeviceConfig;
+import android.sysprop.TelephonyProperties;
 import android.telephony.AnomalyReporter;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
@@ -204,11 +205,9 @@
     public static final String EXTRA_FAILURE_REASON =
             "com.android.phone.slice.extra.FAILURE_REASON";
     /**
-     * Extra for the application name requesting to purchase the premium capability
-     * from the slice purchase application.
+     * Extra for the user's carrier.
      */
-    public static final String EXTRA_REQUESTING_APP_NAME =
-            "com.android.phone.slice.extra.REQUESTING_APP_NAME";
+    public static final String EXTRA_CARRIER = "com.android.phone.slice.extra.CARRIER";
     /**
      * Extra for the canceled PendingIntent that the slice purchase application can send as a
      * response if the performance boost notification or WebView was canceled by the user.
@@ -497,11 +496,10 @@
                 break;
             }
             case EVENT_START_SLICE_PURCHASE_APP: {
-                int capability = msg.arg1;
-                String appName = (String) msg.obj;
-                logd("EVENT_START_SLICE_PURCHASE_APP: " + appName + " requests capability "
+                int capability = (int) msg.obj;
+                logd("EVENT_START_SLICE_PURCHASE_APP: "
                         + TelephonyManager.convertPremiumCapabilityToString(capability));
-                onStartSlicePurchaseApplication(capability, appName);
+                onStartSlicePurchaseApplication(capability);
                 break;
             }
             case EVENT_PURCHASE_TIMEOUT: {
@@ -565,13 +563,11 @@
      * Purchase the given premium capability from the carrier.
      *
      * @param capability The premium capability to purchase.
-     * @param appName The name of the application requesting premium capabilities.
      * @param onComplete The callback message to send when the purchase request is complete.
      */
     public synchronized void purchasePremiumCapability(
-            @TelephonyManager.PremiumCapability int capability, @NonNull String appName,
-            @NonNull Message onComplete) {
-        logd("purchasePremiumCapability: " + appName + " requests capability "
+            @TelephonyManager.PremiumCapability int capability, @NonNull Message onComplete) {
+        logd("purchasePremiumCapability: "
                 + TelephonyManager.convertPremiumCapabilityToString(capability));
         // Check whether the premium capability can be purchased.
         if (!arePremiumCapabilitiesSupportedByDevice()) {
@@ -627,8 +623,7 @@
         // All state checks passed. Mark purchase pending and start the slice purchase application.
         // Process through the handler since this method is synchronized.
         mPendingPurchaseCapabilities.put(capability, onComplete);
-        sendMessage(obtainMessage(EVENT_START_SLICE_PURCHASE_APP, capability, 0 /* unused */,
-                appName));
+        sendMessage(obtainMessage(EVENT_START_SLICE_PURCHASE_APP, capability));
     }
 
     private void sendPurchaseResult(@TelephonyManager.PremiumCapability int capability,
@@ -687,8 +682,8 @@
         }
     }
 
-    private void onStartSlicePurchaseApplication(@TelephonyManager.PremiumCapability int capability,
-            @NonNull String appName) {
+    private void onStartSlicePurchaseApplication(
+            @TelephonyManager.PremiumCapability int capability) {
         PremiumNetworkEntitlementResponse premiumNetworkEntitlementResponse =
                 mPremiumNetworkEntitlementApi.checkEntitlementStatus(capability);
 
@@ -721,7 +716,8 @@
         }
 
         String purchaseUrl = getPurchaseUrl(premiumNetworkEntitlementResponse);
-        if (TextUtils.isEmpty(purchaseUrl)) {
+        String carrier = getSimOperator();
+        if (TextUtils.isEmpty(purchaseUrl) || TextUtils.isEmpty(carrier)) {
             handlePurchaseResult(capability,
                     PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED, false);
             return;
@@ -753,7 +749,7 @@
         intent.putExtra(EXTRA_SUB_ID, mPhone.getSubId());
         intent.putExtra(EXTRA_PREMIUM_CAPABILITY, capability);
         intent.putExtra(EXTRA_PURCHASE_URL, purchaseUrl);
-        intent.putExtra(EXTRA_REQUESTING_APP_NAME, appName);
+        intent.putExtra(EXTRA_CARRIER, carrier);
         intent.putExtra(EXTRA_INTENT_CANCELED, createPendingIntent(
                 ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED, capability, false));
         intent.putExtra(EXTRA_INTENT_CARRIER_ERROR, createPendingIntent(
@@ -805,6 +801,20 @@
     }
 
     /**
+     * Get the SIM operator. This is the carrier name from the SIM rather than from the network,
+     * which will be the same regardless of whether the user is roaming or not.
+     *
+     * @return The operator name from the SIM.
+     */
+    @VisibleForTesting
+    @Nullable public String getSimOperator() {
+        if (mPhone.getPhoneId() < TelephonyProperties.icc_operator_alpha().size()) {
+            return TelephonyProperties.icc_operator_alpha().get(mPhone.getPhoneId());
+        }
+        return null;
+    }
+
+    /**
      * Create the PendingIntent to allow the slice purchase application to send back responses.
      *
      * @param action The action that will be sent for this PendingIntent
@@ -819,6 +829,7 @@
         Intent intent = new Intent(action);
         intent.putExtra(EXTRA_PHONE_ID, mPhone.getPhoneId());
         intent.putExtra(EXTRA_PREMIUM_CAPABILITY, capability);
+        intent.setPackage(mPhone.getContext().getPackageName());
         return PendingIntent.getBroadcast(mPhone.getContext(), capability, intent,
                 PendingIntent.FLAG_CANCEL_CURRENT
                         | (mutable ? PendingIntent.FLAG_MUTABLE : PendingIntent.FLAG_IMMUTABLE));
@@ -1028,6 +1039,11 @@
     }
 
     private boolean isNetworkAvailable() {
+        if (mPhone.getServiceState().getDataRoaming()) {
+            logd("Network unavailable because it is roaming.");
+            return false;
+        }
+
         // TODO (b/251558673): Create a listener for data network type changed to dismiss
         //  notification and activity when the network is no longer available.
         switch (mPhone.getServiceState().getDataNetworkType()) {
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 0b71feb..7d7d949 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -590,36 +590,41 @@
             new DomainSelectionConnection.DomainSelectionConnectionCallback() {
                 @Override
                 public void onSelectionTerminated(@DisconnectCauses int cause) {
-                    Log.v(this, "Call domain selection terminated.");
-                    if (mDomainSelectionConnection != null) {
-                        mDomainSelectionConnection = null;
-                    }
-
-                    if (mNormalCallConnection != null) {
-                        // TODO: To support ShowPreciseFailedCause,
-                        //  TelephonyConnection.getShowPreciseFailedCause API should be added.
-
-                        // If cause is NOT_VALID then, it's a redial cancellation and use cause
-                        // code from original connection.
-                        com.android.internal.telephony.Connection connection =
-                                mNormalCallConnection.getOriginalConnection();
-                        if (connection != null) {
-                            if (cause == android.telephony.DisconnectCause.NOT_VALID) {
-                                cause = connection.getDisconnectCause();
+                    mDomainSelectionMainExecutor.execute(new Runnable() {
+                        int mCause = cause;
+                        @Override
+                        public void run() {
+                            Log.v(this, "Call domain selection terminated.");
+                            if (mDomainSelectionConnection != null) {
+                                mDomainSelectionConnection = null;
                             }
 
-                            String reason = connection.getVendorDisconnectCause();
+                            if (mNormalCallConnection != null) {
+                                // TODO: To support ShowPreciseFailedCause, TelephonyConnection
+                                //  .getShowPreciseFailedCause API should be added.
 
-                            mNormalCallConnection.setTelephonyConnectionDisconnected(
-                                    mDisconnectCauseFactory.toTelecomDisconnectCause(
-                                            cause, reason));
-                            Log.d(this, "Call connection closed. Cause: " + cause
-                                    + " Reason: " + reason);
+                                // If cause is NOT_VALID then, it's a redial cancellation and
+                                // use cause code from original connection.
+                                com.android.internal.telephony.Connection connection =
+                                        mNormalCallConnection.getOriginalConnection();
+                                if (connection != null) {
+                                    if (mCause == android.telephony.DisconnectCause.NOT_VALID) {
+                                        mCause = connection.getDisconnectCause();
+                                    }
+
+                                    String reason = connection.getVendorDisconnectCause();
+                                    int phoneId = mNormalCallConnection.getPhone().getPhoneId();
+                                    mNormalCallConnection.setTelephonyConnectionDisconnected(
+                                            mDisconnectCauseFactory.toTelecomDisconnectCause(
+                                                    mCause, reason, phoneId));
+                                    Log.d(this, "Call connection closed. Cause: " + mCause
+                                            + " Reason: " + reason);
+                                }
+                                mNormalCallConnection.close();
+                                mNormalCallConnection = null;
+                            }
                         }
-                        mNormalCallConnection.close();
-                        mNormalCallConnection = null;
-                    }
-
+                    });
                 }
             };
 
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
index 9aaf6da..ae4659d 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -125,7 +125,7 @@
     private int mScanTimeout;
     private int mMaxNumOfVoWifiTries;
     private @CarrierConfigManager.ImsEmergency.EmergencyScanType int mPreferredNetworkScanType;
-    private int mCallSetupTimerOnCurrentRatSec;
+    private int mCallSetupTimerOnCurrentRat;
     private boolean mRequiresImsRegistration;
     private boolean mRequiresVoLteEnabled;
     private boolean mLtePreferredAfterNrFailure;
@@ -221,8 +221,14 @@
 
     @Override
     public void reselectDomain(SelectionAttributes attr) {
-        logi("reselectDomain tryCsWhenPsFails=" + mTryCsWhenPsFails + ", attr=" + attr);
+        logi("reselectDomain attr=" + attr);
         mSelectionAttributes = attr;
+        post(() -> { reselectDomain(); });
+    }
+
+    private void reselectDomain() {
+        logi("reselectDomain tryCsWhenPsFails=" + mTryCsWhenPsFails);
+
         if (mTryCsWhenPsFails) {
             mTryCsWhenPsFails = false;
             mCsNetworkType = getSelectableCsNetworkType();
@@ -241,10 +247,7 @@
 
         if (mLastTransportType == TRANSPORT_TYPE_WLAN) {
             // Dialing over Wi-Fi failed. Try scanning cellular networks.
-            onWwanSelected(() -> {
-                requestScan(true, false, true);
-                mDomainSelected = false;
-            });
+            onWwanSelected(this::reselectDomainInternal);
             return;
         }
 
@@ -252,6 +255,13 @@
         mDomainSelected = false;
     }
 
+    private void reselectDomainInternal() {
+        post(() -> {
+            requestScan(true, false, true);
+            mDomainSelected = false;
+        });
+    }
+
     @Override
     public void finishSelection() {
         logi("finishSelection");
@@ -343,11 +353,11 @@
         mDomainPreferenceRoam = b.getIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY);
         mPreferImsWhenCallsOnCs = b.getBoolean(
                 KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL);
-        mScanTimeout = b.getInt(KEY_EMERGENCY_SCAN_TIMER_SEC_INT);
+        mScanTimeout = b.getInt(KEY_EMERGENCY_SCAN_TIMER_SEC_INT) * 1000;
         mMaxNumOfVoWifiTries = b.getInt(KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT);
         mPreferredNetworkScanType = b.getInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT);
-        mCallSetupTimerOnCurrentRatSec = b.getInt(
-                KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT);
+        mCallSetupTimerOnCurrentRat = b.getInt(
+                KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT) * 1000;
         mRequiresImsRegistration = b.getBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL);
         mRequiresVoLteEnabled = b.getBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL);
         mLtePreferredAfterNrFailure = b.getBoolean(
@@ -380,7 +390,7 @@
                 + ", maxNumOfVoWifiTries=" + mMaxNumOfVoWifiTries
                 + ", preferredScanType=" + carrierConfigNetworkScanTypeToString(
                         mPreferredNetworkScanType)
-                + ", callSetupTimer=" + mCallSetupTimerOnCurrentRatSec
+                + ", callSetupTimer=" + mCallSetupTimerOnCurrentRat
                 + ", requiresImsReg=" + mRequiresImsRegistration
                 + ", requiresVoLteEnabled=" + mRequiresVoLteEnabled
                 + ", ltePreferredAfterNr=" + mLtePreferredAfterNrFailure
@@ -422,6 +432,10 @@
     }
 
     private void selectDomainInternal() {
+        post(this::selectDomainFromInitialState);
+    }
+
+    private void selectDomainFromInitialState() {
         if (getImsNetworkTypeConfiguration().isEmpty()
                 || (mRequiresVoLteEnabled && !isAdvancedCallingSettingEnabled())) {
             // Emergency call over IMS is not supported.
@@ -896,6 +910,12 @@
 
     private void onWlanSelected() {
         logi("onWlanSelected");
+        if (mLastTransportType == TRANSPORT_TYPE_WLAN) {
+            logi("onWlanSelected ignore duplicated callback");
+            return;
+        }
+
+        mDomainSelected = true;
         mLastTransportType = TRANSPORT_TYPE_WLAN;
         mVoWifiTrialCount++;
         mTransportSelectorCallback.onWlanSelected();
@@ -904,10 +924,8 @@
 
     private void onWwanSelected(Runnable runnable) {
         logi("onWwanSelected");
-        if (mLastTransportType == TRANSPORT_TYPE_WWAN
-                && mWwanSelectorCallback != null) {
-            logi("onWwanSelected already notified");
-            runnable.run();
+        if (mLastTransportType == TRANSPORT_TYPE_WWAN) {
+            logi("onWwanSelected ignore duplicated callback");
             return;
         }
 
diff --git a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java
index 0f120f4..a74e7f0 100644
--- a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java
+++ b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java
@@ -23,7 +23,7 @@
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION;
-import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_OVERRIDDEN;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED;
@@ -271,8 +271,8 @@
                 return "Already purchased";
             case PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS:
                 return "Already in progress";
-            case PURCHASE_PREMIUM_CAPABILITY_RESULT_OVERRIDDEN:
-                return "Overridden";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND:
+                return "Not foreground";
             case PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED:
                 return "User canceled";
             case PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED:
diff --git a/tests/src/com/android/phone/Telephony2gUpdaterTest.java b/tests/src/com/android/phone/Telephony2gUpdaterTest.java
index 3443767..3684f30 100644
--- a/tests/src/com/android/phone/Telephony2gUpdaterTest.java
+++ b/tests/src/com/android/phone/Telephony2gUpdaterTest.java
@@ -16,13 +16,17 @@
 
 package com.android.phone;
 
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Intent;
 import android.os.UserManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -35,22 +39,33 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
 public class Telephony2gUpdaterTest extends TelephonyTestBase {
+    private static final long DRAIN_TIMEOUT = 10;
     private Telephony2gUpdater mTelephony2gUpdater;
+    private SubscriptionManager.OnSubscriptionsChangedListener mChangedListener;
     private Executor mExecutor;
+    private CountDownLatch mLatch;
 
     private UserManager mMockUserManager;
     private TelephonyManager mMockTelephonyManager;
     private SubscriptionManager mMockSubscriptionManager;
 
+    // Set up to be returned from mMockSubscriptionManager.getCompleteActiveSubscriptionInfoList()
+    // Updates will be reflected in subsequent calls to the mock method.
+    private List<SubscriptionInfo> mCurrentSubscriptions;
+
     // 2G Bitmask is 0b10000000_01001011
     private static final long BASE_NETWORK = 0b11111111_11111111;
     private static final long EXPECTED_DISABLED = 0b01111111_10110100;
@@ -65,89 +80,247 @@
         mMockUserManager = mContext.getSystemService(UserManager.class);
         mMockSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
 
+        mCurrentSubscriptions = new ArrayList<>();
+        setupMutableSubscriptionInfoMock();
+
         mExecutor = Executors.newSingleThreadExecutor();
-        mTelephony2gUpdater = new Telephony2gUpdater(mExecutor,
-                getTestContext(), BASE_NETWORK);
+        mTelephony2gUpdater = new Telephony2gUpdater(mExecutor, getTestContext(), BASE_NETWORK);
+        mTelephony2gUpdater.init();
+        ArgumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener> argument =
+                ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class);
+        verify(mMockSubscriptionManager).addOnSubscriptionsChangedListener(any(Executor.class),
+                argument.capture());
+        mChangedListener = argument.getValue();
     }
 
     @Test
-    public void handleUserRestrictionsChanged_noSubscriptions_noAllowedNetworksChanged() {
-        when(mMockSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
-                new ArrayList<>());
-        mTelephony2gUpdater.handleUserRestrictionsChanged(getTestContext());
+    public void onSubscriptionsChanged_noSubscriptions_noAllowedNetworksChanged() {
+        triggerOnSubscriptionChangedAndWait();
         verify(mMockTelephonyManager, never()).setAllowedNetworkTypesForReason(anyInt(), anyInt());
     }
 
     @Test
-    public void handleUserRestrictionsChanged_nullSubscriptions_noAllowedNetworksChanged() {
-        when(mMockSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(null);
-        mTelephony2gUpdater.handleUserRestrictionsChanged(getTestContext());
-        verify(mMockTelephonyManager, never()).setAllowedNetworkTypesForReason(anyInt(), anyInt());
-    }
+    public void onSubscriptionsChanged_oneSubscription_allowedNetworksUpdated() {
+        TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
+        triggerOnSubscriptionChangedAndWait();
 
-    @Test
-    public void handleUserRestrictionsChanged_oneSubscription_allowedNetworksUpdated() {
-        TelephonyManager tmSubscription1 = mock(TelephonyManager.class);
-        when(mMockSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
-                Collections.singletonList(getSubInfo(1)));
-        when(mMockTelephonyManager.createForSubscriptionId(1)).thenReturn(tmSubscription1);
-        when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
-                true);
-
-        mTelephony2gUpdater.handleUserRestrictionsChanged(getTestContext());
-
-        System.out.println(TelephonyManager.convertNetworkTypeBitmaskToString(11L));
         verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
-                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
     }
 
     @Test
-    public void handleUserRestrictionsChanged_manySubscriptionsDisallow2g_allowedNetworkUpdated() {
-
-        // Two subscriptions are available
-        when(mMockSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
-                Arrays.asList(getSubInfo(1), getSubInfo(2)));
-        TelephonyManager tmSubscription1 = mock(TelephonyManager.class);
-        TelephonyManager tmSubscription2 = mock(TelephonyManager.class);
-        when(mMockTelephonyManager.createForSubscriptionId(1)).thenReturn(tmSubscription1);
-        when(mMockTelephonyManager.createForSubscriptionId(2)).thenReturn(tmSubscription2);
+    public void onSubscriptionsChanged_manySubscriptionsDisallow2g_allowedNetworkUpdated() {
         // 2g is disallowed
         when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
                 true);
+        triggerBroadcastReceiverAndWait();
 
-        mTelephony2gUpdater.handleUserRestrictionsChanged(getTestContext());
+        TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
+        TelephonyManager tmSubscription2 = addSubscriptionAndGetMock(1002);
+
+        triggerOnSubscriptionChangedAndWait();
 
         verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
                 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+        verify(tmSubscription2, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+    }
+
+    @Test
+    public void onSubscriptionsChanged_noNewSubscriptions_noAllowedNetworksChanged() {
+        when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
+                true);
+        triggerBroadcastReceiverAndWait();
+
+        TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
+
+        triggerOnSubscriptionChangedAndWait();
+        triggerOnSubscriptionChangedAndWait();
+
+        // subscriptions were updated twice, but we have no new subIds so we only expect one update
         verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
                 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
     }
 
     @Test
-    public void handleUserRestrictionsChanged_manySubscriptionsAllow2g_allowedNetworkUpdated() {
+    public void onSubscriptionsChanged_removeSubscription_noAdditionalNetworkChanges() {
+        // We start with 2 subIds
+        TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
+        TelephonyManager tmSubscription2 = addSubscriptionAndGetMock(1002);
 
-        // Two subscriptions are available
-        when(mMockSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
-                Arrays.asList(getSubInfo(1), getSubInfo(2)));
-        TelephonyManager tmSubscription1 = mock(TelephonyManager.class);
-        TelephonyManager tmSubscription2 = mock(TelephonyManager.class);
-        when(mMockTelephonyManager.createForSubscriptionId(1)).thenReturn(tmSubscription1);
-        when(mMockTelephonyManager.createForSubscriptionId(2)).thenReturn(tmSubscription2);
+        triggerOnSubscriptionChangedAndWait();
 
-        // 2g is allowed
+        // 2g is still enabled since the default is to not set the user restriction
+        verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+        verify(tmSubscription2, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+
+
+        mCurrentSubscriptions.remove(1);
+        triggerOnSubscriptionChangedAndWait();
+
+        // Subscriptions have changed, but we've only removed a subscription so there should be no
+        // extra updates to allowed network types
+        verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+        verify(tmSubscription2, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+    }
+
+    @Test
+    public void onSubscriptionsChanged_removeSubscriptionAndReAdd() {
+        when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
+                true);
+        triggerBroadcastReceiverAndWait();
+
+        TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
+        triggerOnSubscriptionChangedAndWait();
+        mCurrentSubscriptions.remove(0);
+        triggerOnSubscriptionChangedAndWait();
+        mCurrentSubscriptions.add(getSubInfo(1001));
+        triggerOnSubscriptionChangedAndWait();
+
+        // subscriptions were updated thrice, but one of those updates removed a subscription
+        // such that the sub list was empty, so we only expect an update on the first and last
+        // updates.
+        verify(tmSubscription1, times(2)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+    }
+
+    @Test
+    public void onSubscriptionsChanged_addSubscription_updateAllowedNetworks() {
+        // We start with 2 subIds and update subscriptions
+        TelephonyManager tmSubscription1 = addSubscriptionAndGetMock(1001);
+        TelephonyManager tmSubscription2 = addSubscriptionAndGetMock(1002);
+        triggerOnSubscriptionChangedAndWait();
+
+        // Then add a subId and update subscriptions again
+        TelephonyManager tmSubscription3 = addSubscriptionAndGetMock(1003);
+        triggerOnSubscriptionChangedAndWait();
+
+        // we only need to update the new subscription
+        verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+        verify(tmSubscription2, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+        verify(tmSubscription3, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+    }
+
+    @Test
+    public void onUserRestrictionUnchanged_noChangeToRestriction_noAllowedNetworksUpdated() {
+        TelephonyManager tmSubscription = addSubscriptionAndGetMock(1001);
+        triggerOnSubscriptionChangedAndWait();
+        // precondition: we've updated allowed networks to the default (2g enabled)
+        verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+        verify(tmSubscription, never()).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+
+        when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
+                true);
+        triggerBroadcastReceiverAndWait();
+        triggerBroadcastReceiverAndWait();
+
+        // expect we only updated once even though we got two broadcasts for user restriction
+        // updates
+        verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+        // extra check to ensure we haven't also somehow updated back to enabled along the way
+        verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+    }
+
+    @Test
+    public void onUserRestrictionChanged_restrictionChanged_allowedNetworksUpdated() {
+        // precondition: we've updated allowed networks to the default (2g enabled)
+        TelephonyManager tmSubscription = addSubscriptionAndGetMock(1001);
+        triggerOnSubscriptionChangedAndWait();
+        verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+        verify(tmSubscription, never()).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+
+        // update the user restriction to disallow 2g
+        reset(tmSubscription);
+        when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
+                true);
+        triggerBroadcastReceiverAndWait();
+        verify(tmSubscription, never()).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+        verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+
+
+        // update the user restriction to allow 2g again
+        reset(tmSubscription);
         when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_CELLULAR_2G)).thenReturn(
                 false);
-
-        mTelephony2gUpdater.handleUserRestrictionsChanged(getTestContext());
-
-        verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
+        triggerBroadcastReceiverAndWait();
+        verify(tmSubscription, times(1)).setAllowedNetworkTypesForReason(
                 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
-        verify(tmSubscription1, times(1)).setAllowedNetworkTypesForReason(
-                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_ENABLED);
+        verify(tmSubscription, never()).setAllowedNetworkTypesForReason(
+                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS, EXPECTED_DISABLED);
+
     }
 
     private SubscriptionInfo getSubInfo(int id) {
         return new SubscriptionInfo(id, "890126042XXXXXXXXXXX", 0, "T-mobile", "T-mobile", 0, 255,
                 "12345", 0, null, "310", "260", "156", false, null, null);
     }
+
+    private void triggerOnSubscriptionChangedAndWait() {
+        mExecutor.execute(() -> mChangedListener.onSubscriptionsChanged());
+        drainSingleThreadedExecutor();
+    }
+
+    private void triggerBroadcastReceiverAndWait() {
+        mTelephony2gUpdater.onReceive(mContext, new Intent());
+        drainSingleThreadedExecutor();
+    }
+
+    /**
+     * Wait for all tasks on executor up to the point of invocation to drain, then return.
+     *
+     * This helper takes advantage of the fact that we're using an immutable single threaded
+     * executor that guarantees tasks are executed in the order they are enqueued. It enqueues a
+     * task that decrements a latch and then waits on that task to finish. By definition, once the
+     * test task finishes, all previously enqueued tasks will have also completed.
+     */
+    private void drainSingleThreadedExecutor() {
+        resetExecutorLatch();
+        mExecutor.execute(() -> mLatch.countDown());
+        try {
+            mLatch.await(DRAIN_TIMEOUT, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    private void resetExecutorLatch() {
+        mLatch = new CountDownLatch(1);
+    }
+
+    /**
+     * Helper that allows you to update subInfo and have that change reflected on subsequent calls
+     * to {@link SubscriptionManager#getCompleteActiveSubscriptionInfoList()}
+     */
+    private void setupMutableSubscriptionInfoMock() {
+        var answer = new Answer<List<SubscriptionInfo>>() {
+            @Override
+            public List<SubscriptionInfo> answer(InvocationOnMock invocation) throws Throwable {
+                return mCurrentSubscriptions;
+            }
+        };
+        when(mMockSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenAnswer(answer);
+    }
+
+    private TelephonyManager addSubscriptionAndGetMock(int subId) {
+        mCurrentSubscriptions.add(getSubInfo(subId));
+        TelephonyManager tmSubscription = mock(TelephonyManager.class);
+        when(mMockTelephonyManager.createForSubscriptionId(subId)).thenReturn(tmSubscription);
+        return tmSubscription;
+    }
+
 }
diff --git a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
index e2ebac0..921babb 100644
--- a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
+++ b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
@@ -70,7 +70,7 @@
 
 @RunWith(AndroidJUnit4.class)
 public class SlicePurchaseControllerTest extends TelephonyTestBase {
-    private static final String TAG = "SlicePurchaseControllerTest";
+    private static final String CARRIER = "Some Carrier";
     private static final String DAILY_NOTIFICATION_COUNT_KEY = "daily_notification_count0";
     private static final String MONTHLY_NOTIFICATION_COUNT_KEY = "monthly_notification_count0";
     private static final int YEAR = 2000;
@@ -148,6 +148,7 @@
         mSlicePurchaseController = spy(slicePurchaseController);
         doReturn(null).when(mSlicePurchaseController).createPendingIntent(
                 anyString(), anyInt(), anyBoolean());
+        doReturn(CARRIER).when(mSlicePurchaseController).getSimOperator();
         replaceInstance(SlicePurchaseController.class, "sInstances", mSlicePurchaseController,
                 Map.of(PHONE_ID, mSlicePurchaseController));
         replaceInstance(SlicePurchaseController.class, "mPremiumNetworkEntitlementApi",
@@ -272,8 +273,7 @@
     @Test
     public void testPurchasePremiumCapabilityResultFeatureNotSupported() {
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
                 mResult);
@@ -283,8 +283,7 @@
                 .getCachedAllowedNetworkTypesBitmask();
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
                 mResult);
@@ -296,8 +295,7 @@
                 .getCachedAllowedNetworkTypesBitmask();
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED, mResult);
 
@@ -308,8 +306,7 @@
                 SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
                 mResult);
@@ -325,8 +322,7 @@
                 SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(
                 TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
@@ -336,8 +332,7 @@
         doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertNotEquals(
                 TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
@@ -355,8 +350,7 @@
         doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
                 mResult);
@@ -365,8 +359,7 @@
         doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mServiceState).getDataNetworkType();
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
                 mResult);
@@ -385,8 +378,7 @@
         doReturn(null).when(mPremiumNetworkEntitlementApi).checkEntitlementStatus(anyInt());
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED,
                 mResult);
@@ -398,8 +390,7 @@
                 .checkEntitlementStatus(anyInt());
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
                 mResult);
@@ -409,8 +400,7 @@
                 PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS;
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
                 mResult);
@@ -425,16 +415,14 @@
                 PURCHASE_CONDITION_TIMEOUT);
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED,
                 mResult);
 
         // retry to verify throttled
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
 
@@ -450,16 +438,14 @@
         sendValidPurchaseRequest();
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
                 mResult);
 
         // retry to verify same result
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
                 mResult);
@@ -487,8 +473,7 @@
         testPurchasePremiumCapabilityResultSuccess();
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP,
                 mResult);
@@ -507,16 +492,14 @@
         sendNetworkSlicingConfig(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, true);
 
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
                 mResult);
 
         // retry to verify same result
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
                 mResult);
@@ -537,8 +520,7 @@
 
         // retry to verify throttled
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
 
@@ -565,8 +547,7 @@
 
         // retry to verify throttled
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
 
@@ -596,8 +577,7 @@
 
         // retry to verify throttled
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
 
@@ -660,8 +640,7 @@
 
         // retry to verify throttled
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
 
@@ -678,8 +657,7 @@
 
         // retry to verify throttled
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
     }
@@ -741,8 +719,7 @@
 
         // send purchase request
         mSlicePurchaseController.purchasePremiumCapability(
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                mHandler.obtainMessage());
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
 
         // verify that the purchase request was sent successfully
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index e1de0ab..3f6ce98 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -903,6 +903,39 @@
         verify(mTransportSelectorCallback, times(1)).onWlanSelected();
     }
 
+    @Test
+    public void testIgnoreDuplicatedCallbacks() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(true);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsService(true);
+
+        verify(mTransportSelectorCallback, times(1)).onWwanSelected(any());
+
+        // duplicated event
+        unsolBarringInfoChanged(true);
+
+        // ignore duplicated callback, no change in interaction
+        verify(mTransportSelectorCallback, times(1)).onWwanSelected(any());
+
+        mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+        verify(mTransportSelectorCallback, times(1)).onWlanSelected();
+
+        // duplicated event
+        mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
+
+        // ignore duplicated callback, no change in interaction
+        verify(mTransportSelectorCallback, times(1)).onWlanSelected();
+    }
+
     private void createSelector(int subId) throws Exception {
         mDomainSelector = new EmergencyCallDomainSelector(
                 mContext, SLOT_0, subId, mHandlerThread.getLooper(),
@@ -913,10 +946,12 @@
     }
 
     private void verifyCsDialed() {
+        processAllMessages();
         verify(mWwanSelectorCallback, times(1)).onDomainSelected(eq(DOMAIN_CS));
     }
 
     private void verifyPsDialed() {
+        processAllMessages();
         verify(mWwanSelectorCallback, times(1)).onDomainSelected(eq(DOMAIN_PS));
     }
 
@@ -929,6 +964,7 @@
     }
 
     private void verifyScanPreferred(int scanType, int expectedPreferredAccessNetwork) {
+        processAllMessages();
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
                 any(), eq(scanType), any(), any());
         assertEquals(expectedPreferredAccessNetwork, (int) mAccessNetwork.get(0));