Merge "Restarting the device must be triggered even in the airplane mode"
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index e9a564e..ec2acf4 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -331,6 +331,10 @@
   RAT_IWLAN = 18;
 
   RAT_LTE_CA = 19;
+
+  RAT_NR_NSA = 20;
+
+  RAT_NR_SA = 21;
 }
 
 // The information about IMS errors
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index d0c3184..e112068 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -450,7 +450,7 @@
                     .setContentTitle(title)
                     .setStyle(new Notification.BigTextStyle().bigText(details))
                     .setContentText(details)
-                    .setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
+                    .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT)
                     .setContentIntent(settingsIntent);
         }
     }
@@ -525,7 +525,7 @@
                     .setContentTitle(title)
                     .setStyle(new Notification.BigTextStyle().bigText(details))
                     .setContentText(details)
-                    .setChannel(NotificationChannelController.CHANNEL_ID_WFC);
+                    .setChannelId(NotificationChannelController.CHANNEL_ID_WFC);
         }
     }
 }
diff --git a/src/java/com/android/internal/telephony/CellBroadcastHandler.java b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
index 87ecdc0..08be268 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastHandler.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
@@ -66,7 +66,7 @@
 
     private final LocalLog mLocalLog = new LocalLog(100);
 
-    protected static final Uri CELL_BROADCAST_URI = Uri.parse("content://cellbroadcasts_fwk");
+    protected static final Uri CELL_BROADCAST_URI = Uri.parse("content://cellbroadcasts");
 
     /** Uses to request the location update. */
     public final LocationRequester mLocationRequester;
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 150b29b..3d66b60 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -35,7 +35,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.IPackageManager;
 import android.database.Cursor;
 import android.database.SQLException;
 import android.net.Uri;
@@ -47,10 +46,8 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.storage.StorageManager;
 import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
 import android.service.carrier.CarrierMessagingService;
@@ -665,18 +662,18 @@
             return Intents.RESULT_SMS_HANDLED;
         }
 
-        // onlyCore indicates if the device is in cryptkeeper
-        boolean onlyCore = false;
-        try {
-            onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
-                    isOnlyCoreApps();
-        } catch (RemoteException e) {
-        }
-        if (onlyCore) {
-            // Device is unable to receive SMS in encrypted state
-            log("Received a short message in encrypted state. Rejecting.");
-            return Intents.RESULT_SMS_GENERIC_ERROR;
-        }
+//        // onlyCore indicates if the device is in cryptkeeper
+//        boolean onlyCore = false;
+//        try {
+//            onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
+//                    isOnlyCoreApps();
+//        } catch (RemoteException e) {
+//        }
+//        if (onlyCore) {
+//            // Device is unable to receive SMS in encrypted state
+//            log("Received a short message in encrypted state. Rejecting.");
+//            return Intents.RESULT_SMS_GENERIC_ERROR;
+//        }
 
         int result = dispatchMessageRadioSpecific(smsb);
 
@@ -1045,9 +1042,9 @@
     @UnsupportedAppUsage
     private void showNewMessageNotification() {
         // Do not show the notification on non-FBE devices.
-        if (!StorageManager.isFileEncryptedNativeOrEmulated()) {
-            return;
-        }
+        // if (!StorageManager.isFileEncryptedNativeOrEmulated()) {
+        //     return;
+        // }
         log("Show new message notification.");
         PendingIntent intent = PendingIntent.getBroadcast(
             mContext,
diff --git a/src/java/com/android/internal/telephony/IntentBroadcaster.java b/src/java/com/android/internal/telephony/IntentBroadcaster.java
index 7528819..e211c24 100644
--- a/src/java/com/android/internal/telephony/IntentBroadcaster.java
+++ b/src/java/com/android/internal/telephony/IntentBroadcaster.java
@@ -86,13 +86,13 @@
      * Wrapper for ActivityManager.broadcastStickyIntent() that also stores intent to be rebroadcast
      * on USER_UNLOCKED
      */
-    public void broadcastStickyIntent(Intent intent, int slotId) {
+    public void broadcastStickyIntent(Intent intent, int phoneId) {
         logd("Broadcasting and adding intent for rebroadcast: " + intent.getAction() + " "
                 + intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)
-                + " for slotId " + slotId);
+                + " for phoneId " + phoneId);
         synchronized (mRebroadcastIntents) {
             ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
-            mRebroadcastIntents.put(slotId, intent);
+            mRebroadcastIntents.put(phoneId, intent);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index b284799..1c9a7e0 100755
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -454,41 +454,41 @@
      */
     private synchronized void updateLocale() {
         // If MCC is available from network service state, use it first.
-        String mcc = null;
         String countryIso = getCarrierCountry();
-        boolean isBogusMcc = false;
+        String countryIsoDebugInfo = "getCarrierCountry()";
 
         if (!TextUtils.isEmpty(mOperatorNumeric)) {
             try {
-                mcc = mOperatorNumeric.substring(0, 3);
+                String mcc = mOperatorNumeric.substring(0, 3);
                 countryIso = MccTable.countryCodeForMcc(mcc);
-                if (!TextUtils.isEmpty(mcc) && TextUtils.isEmpty(countryIso)) {
-                    isBogusMcc = true;
-                }
+                countryIsoDebugInfo = "OperatorNumeric(" + mOperatorNumeric
+                        + "): MccTable.countryCodeForMcc(\"" + mcc + "\")";
             } catch (StringIndexOutOfBoundsException ex) {
-                loge("updateLocale: Can't get country from operator numeric. mcc = "
-                        + mcc + ". ex=" + ex);
+                loge("updateLocale: Can't get country from operator numeric. mOperatorNumeric = "
+                        + mOperatorNumeric + ". ex=" + ex);
             }
         }
 
         // If for any reason we can't get country from operator numeric, try to get it from cell
         // info.
         if (TextUtils.isEmpty(countryIso)) {
-            mcc = getMccFromCellInfo();
+            String mcc = getMccFromCellInfo();
             countryIso = MccTable.countryCodeForMcc(mcc);
+            countryIsoDebugInfo = "CellInfo: MccTable.countryCodeForMcc(\"" + mcc + "\")";
         }
 
         if (mCountryOverride != null) {
             countryIso = mCountryOverride;
+            countryIsoDebugInfo = "mCountryOverride = \"" + mCountryOverride + "\"";
             log("Override current country to " + mCountryOverride);
         }
 
-        log("updateLocale: mcc = " + mcc + ", country = " + countryIso
-                + ", isBogusMcc = " + isBogusMcc);
-        boolean countryChanged = false;
+        log("updateLocale: countryIso = " + countryIso
+                + ", countryIsoDebugInfo = " + countryIsoDebugInfo);
         if (!Objects.equals(countryIso, mCurrentCountryIso)) {
             String msg = "updateLocale: Change the current country to \"" + countryIso
-                    + "\", mcc = " + mcc + ", mCellInfoList = " + mCellInfoList;
+                    + "\", countryIsoDebugInfo = " + countryIsoDebugInfo
+                    + ", mCellInfoList = " + mCellInfoList;
             log(msg);
             mLocalLog.log(msg);
             mCurrentCountryIso = countryIso;
@@ -500,15 +500,21 @@
             intent.putExtra(TelephonyManager.EXTRA_NETWORK_COUNTRY, countryIso);
             SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
             mPhone.getContext().sendBroadcast(intent);
-
-            countryChanged = true;
         }
 
-        // For bogus mcc, the countryIso is always empty, it should be marked as available.
-        if (TextUtils.isEmpty(countryIso) && !isBogusMcc) {
-            mNitzStateMachine.handleNetworkCountryCodeUnavailable();
+        // For a test cell, the NitzStateMachine requires handleCountryDetected("") to pass
+        // compliance tests. http://b/142840879
+        boolean isTestMcc = false;
+        if (!TextUtils.isEmpty(mOperatorNumeric)) {
+            if (mOperatorNumeric.startsWith("001")) {
+                isTestMcc = true;
+                countryIso = "";
+            }
+        }
+        if (TextUtils.isEmpty(countryIso) && !isTestMcc) {
+            mNitzStateMachine.handleCountryUnavailable();
         } else {
-            mNitzStateMachine.handleNetworkCountryCodeSet(countryChanged);
+            mNitzStateMachine.handleCountryDetected(countryIso);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/NitzStateMachine.java b/src/java/com/android/internal/telephony/NitzStateMachine.java
index 1ab306e..ec5aaa6 100644
--- a/src/java/com/android/internal/telephony/NitzStateMachine.java
+++ b/src/java/com/android/internal/telephony/NitzStateMachine.java
@@ -17,13 +17,11 @@
 package com.android.internal.telephony;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.telephony.TelephonyManager;
 import android.util.TimestampedValue;
 
 import com.android.internal.util.IndentingPrintWriter;
@@ -40,13 +38,12 @@
 public interface NitzStateMachine {
 
     /**
-     * Called when the network country is set on the Phone. Although set, the network country code
-     * may be invalid.
+     * Called when the country suitable for time zone detection is detected.
      *
-     * @param countryChanged true when the country code is known to have changed, false if it
-     *     probably hasn't
+     * @param countryIsoCode the countryIsoCode to use for time zone detection, may be "" for test
+     *     cells only, otherwise {@link #handleCountryUnavailable()} should be called
      */
-    void handleNetworkCountryCodeSet(boolean countryChanged);
+    void handleCountryDetected(@NonNull String countryIsoCode);
 
     /**
      * Informs the {@link NitzStateMachine} that the network has become available.
@@ -54,10 +51,15 @@
     void handleNetworkAvailable();
 
     /**
-     * Informs the {@link NitzStateMachine} that the country code from network has become
-     * unavailable.
+     * Informs the {@link NitzStateMachine} that the network has become unavailable.
      */
-    void handleNetworkCountryCodeUnavailable();
+    void handleNetworkUnavailable();
+
+    /**
+     * Informs the {@link NitzStateMachine} that any previously detected country supplied via
+     * {@link #handleCountryDetected(String)} is no longer valid.
+     */
+    void handleCountryUnavailable();
 
     /**
      * Handle a new NITZ signal being received.
@@ -103,8 +105,6 @@
          */
         boolean getIgnoreNitz();
 
-        @Nullable String getNetworkCountryIsoForPhone();
-
         /**
          * Returns the same value as {@link SystemClock#elapsedRealtime()}.
          */
@@ -128,16 +128,10 @@
         private static final int NITZ_UPDATE_DIFF_DEFAULT = 2000;
         private final int mNitzUpdateDiff;
 
-        private final Phone mPhone;
-        private final TelephonyManager mTelephonyManager;
         private final ContentResolver mCr;
 
         public DeviceStateImpl(Phone phone) {
-            mPhone = phone;
-
             Context context = phone.getContext();
-            mTelephonyManager =
-                    (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
             mCr = context.getContentResolver();
             mNitzUpdateSpacing =
                     SystemProperties.getInt("ro.nitz_update_spacing", NITZ_UPDATE_SPACING_DEFAULT);
@@ -163,12 +157,6 @@
         }
 
         @Override
-        @Nullable
-        public String getNetworkCountryIsoForPhone() {
-            return mTelephonyManager.getNetworkCountryIso(mPhone.getPhoneId());
-        }
-
-        @Override
         public long elapsedRealtime() {
             return SystemClock.elapsedRealtime();
         }
diff --git a/src/java/com/android/internal/telephony/NitzStateMachineImpl.java b/src/java/com/android/internal/telephony/NitzStateMachineImpl.java
index 04659e0..23152ac 100644
--- a/src/java/com/android/internal/telephony/NitzStateMachineImpl.java
+++ b/src/java/com/android/internal/telephony/NitzStateMachineImpl.java
@@ -56,13 +56,11 @@
     private TimestampedValue<NitzData> mLatestNitzSignal;
 
     /**
-     * Records whether the device should have a country code available via
-     * {@link DeviceState#getNetworkCountryIsoForPhone()}. Before this an NITZ signal
-     * received is (almost always) not enough to determine time zone. On test networks the country
-     * code should be available but can still be an empty string but this flag indicates that the
-     * information available is unlikely to improve.
+     * Records the country to use for time zone detection. It can be a valid ISO 3166 alpha-2 code
+     * (lower case), empty (test network) or null (no country detected). A country code is required
+     * to determine time zone except when on a test network.
      */
-    private boolean mGotCountryCode = false;
+    private String mCountryIsoCode;
 
     /**
      * The last time zone ID that has been determined. It may not have been set as the device time
@@ -81,11 +79,12 @@
 
     /**
      * Boolean is {@code true} if NITZ has been used to determine a time zone (which may not
-     * ultimately have been used due to user settings). Cleared by {@link #handleNetworkAvailable()}
-     * and {@link #handleNetworkCountryCodeUnavailable()}. The flag can be used when historic NITZ
-     * data may no longer be valid. {@code false} indicates it is reasonable to try to set the time
-     * zone using less reliable algorithms than NITZ-based detection such as by just using network
-     * country code.
+     * ultimately have been used due to user settings). Cleared by {@link
+     * #handleNetworkAvailable()}, {@link #handleCountryUnavailable()},
+     * {@link #handleNetworkUnavailable()}, and {@link #handleAirplaneModeChanged(boolean)}. The
+     * flag can be used when historic NITZ data may no longer be valid. {@code false} indicates it
+     * is reasonable to try to set the time zone using less reliable algorithms than NITZ-based
+     * detection such as by just using network country code.
      */
     private boolean mNitzTimeZoneDetectionSuccessful = false;
 
@@ -131,16 +130,16 @@
     }
 
     @Override
-    public void handleNetworkCountryCodeSet(boolean countryChanged) {
-        boolean hadCountryCode = mGotCountryCode;
-        mGotCountryCode = true;
+    public void handleCountryDetected(String countryIsoCode) {
+        String oldCountryIsoCode = mCountryIsoCode;
+        mCountryIsoCode = Objects.requireNonNull(countryIsoCode);
 
-        String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
-        if (!TextUtils.isEmpty(isoCountryCode) && !mNitzTimeZoneDetectionSuccessful) {
-            updateTimeZoneFromNetworkCountryCode(isoCountryCode);
+        if (!TextUtils.isEmpty(countryIsoCode) && !mNitzTimeZoneDetectionSuccessful) {
+            updateTimeZoneFromNetworkCountryCode(countryIsoCode);
         }
 
-        if (mLatestNitzSignal != null && (countryChanged || !hadCountryCode)) {
+        boolean countryChanged = Objects.equals(oldCountryIsoCode, countryIsoCode);
+        if (mLatestNitzSignal != null && (countryChanged || oldCountryIsoCode == null)) {
             updateTimeZoneFromCountryAndNitz();
         }
     }
@@ -149,7 +148,7 @@
         // This method must only be called after mLatestNitzSignal has been set to a non-null
         // value.
         TimestampedValue<NitzData> nitzSignal = Objects.requireNonNull(mLatestNitzSignal);
-        String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+        String isoCountryCode = mCountryIsoCode;
 
         // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never
         // been set which makes it difficult to tell if it's what the user / time zone detection
@@ -171,7 +170,7 @@
             String zoneId;
             if (nitzData.getEmulatorHostTimeZone() != null) {
                 zoneId = nitzData.getEmulatorHostTimeZone().getID();
-            } else if (!mGotCountryCode) {
+            } else if (isoCountryCode == null) {
                 // We don't have a country code so we won't try to look up the time zone.
                 zoneId = null;
             } else if (TextUtils.isEmpty(isoCountryCode)) {
@@ -312,12 +311,37 @@
     }
 
     @Override
-    public void handleNetworkCountryCodeUnavailable() {
+    public void handleNetworkUnavailable() {
+        if (DBG) {
+            Rlog.d(LOG_TAG, "handleNetworkUnavailable: Clearing NITZ and detection state");
+        }
+        // Clear state related to NITZ.
+        mSavedNitzTime = null;
+        mTimeLog.log("handleNetworkUnavailable: NITZ state cleared.");
+
+        mLatestNitzSignal = null;
+        mNitzTimeZoneDetectionSuccessful = false;
+        mSavedTimeZoneId = null;
+        mTimeZoneLog.log("handleNetworkUnavailable: NITZ state cleared.");
+
+        // mSavedTimeZoneId has been cleared but it might be sufficient to detect the time zone
+        // using only the country information that is left.
+        String isoCountryCode = mCountryIsoCode;
+        if (isoCountryCode != null) {
+            if (!TextUtils.isEmpty(isoCountryCode)) {
+                updateTimeZoneFromNetworkCountryCode(isoCountryCode);
+            }
+        }
+    }
+
+    @Override
+    public void handleCountryUnavailable() {
         if (DBG) {
             Rlog.d(LOG_TAG, "handleNetworkCountryCodeUnavailable");
         }
 
-        mGotCountryCode = false;
+        mSavedTimeZoneId = null;
+        mCountryIsoCode = null;
         mNitzTimeZoneDetectionSuccessful = false;
     }
 
@@ -348,8 +372,14 @@
         // principles what the time / time zone is. This assumes calls like handleNetworkAvailable()
         // will be made after airplane mode is re-enabled as the device re-establishes network
         // connectivity.
-        clearTimeDetectionState();
-        clearTimeZoneDetectionState();
+        mSavedNitzTime = null;
+        mTimeLog.log("handleAirplaneModeChanged(" + on + "): Time state cleared.");
+
+        mCountryIsoCode = null;
+        mLatestNitzSignal = null;
+        mNitzTimeZoneDetectionSuccessful = false;
+        mSavedTimeZoneId = null;
+        mTimeZoneLog.log("handleAirplaneModeChanged(" + on + "): Time zone state cleared.");
     }
 
     private void updateTimeFromNitz() {
@@ -446,11 +476,6 @@
         }
     }
 
-    private void clearTimeDetectionState() {
-        mSavedNitzTime = null;
-        mTimeZoneLog.log("clearTimeZoneDetectionState: All time detection state cleared.");
-    }
-
     private void setAndBroadcastNetworkSetTimeZone(String zoneId, String logMessage) {
         logMessage += " [Setting device time zone to zoneId=" + zoneId + "]";
         if (DBG) {
@@ -494,7 +519,7 @@
 
         // Time Zone Detection State
         pw.println(" mLatestNitzSignal=" + mLatestNitzSignal);
-        pw.println(" mGotCountryCode=" + mGotCountryCode);
+        pw.println(" mCountryIsoCode=" + mCountryIsoCode);
         pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId);
         pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful);
 
@@ -554,14 +579,6 @@
         }
     }
 
-    private void clearTimeZoneDetectionState() {
-        mLatestNitzSignal = null;
-        mGotCountryCode = false;
-        mSavedTimeZoneId = null;
-        mNitzTimeZoneDetectionSuccessful = false;
-        mTimeZoneLog.log("clearTimeZoneDetectionState: All time zone detection state cleared.");
-    }
-
     // VisibleForTesting
     public boolean getNitzTimeZoneDetectionSuccessful() {
         return mNitzTimeZoneDetectionSuccessful;
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 561a051..30391ca 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -728,7 +728,7 @@
         mMin = null;
         mPrlVersion = null;
         mIsMinInfoReady = false;
-        mNitzState.handleNetworkCountryCodeUnavailable();
+        mNitzState.handleCountryUnavailable();
         mCellIdentity = null;
         mNewCellIdentity = null;
         mSignalStrengthUpdatedTime = System.currentTimeMillis();
@@ -3014,7 +3014,7 @@
                 mNewSS.setStateOutOfService();
                 mNewCellIdentity = null;
                 setSignalStrengthDefaultValues();
-                mNitzState.handleNetworkCountryCodeUnavailable();
+                mNitzState.handleCountryUnavailable();
                 pollStateDone();
                 break;
 
@@ -3022,7 +3022,7 @@
                 mNewSS.setStateOff();
                 mNewCellIdentity = null;
                 setSignalStrengthDefaultValues();
-                mNitzState.handleNetworkCountryCodeUnavailable();
+                mNitzState.handleCountryUnavailable();
                 // don't poll when device is shutting down or the poll was not modemTrigged
                 // (they sent us new radio data) and current network is not IWLAN
                 if (mDeviceShuttingDown ||
@@ -3320,6 +3320,7 @@
 
         if (hasDeregistered) {
             mNetworkDetachedRegistrants.notifyRegistrants();
+            mNitzState.handleNetworkUnavailable();
         }
 
         if (hasRejectCauseChanged) {
@@ -4203,7 +4204,7 @@
                 .setContentTitle(title)
                 .setStyle(new Notification.BigTextStyle().bigText(details))
                 .setContentText(details)
-                .setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
+                .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT)
                 .build();
 
         NotificationManager notificationManager = (NotificationManager)
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 53d52ad..24bb42d 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -378,7 +378,8 @@
                     + " carrierConfigAccessRules: " + Arrays.toString(carrierConfigAccessRules)
                     + " cardId:" + cardIdToPrint + " publicCardId:" + publicCardId
                     + " isOpportunistic:" + isOpportunistic + " groupUUID:" + groupUUID
-                    + " profileClass:" + profileClass + " subscriptionType: " + subType);
+                    + " profileClass:" + profileClass + " subscriptionType: " + subType
+                    + " carrierConfigAccessRules:" + carrierConfigAccessRules);
         }
 
         // If line1number has been set to a different number, use it instead.
@@ -3276,7 +3277,7 @@
             int subId = info.getSubscriptionId();
             return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId,
                     callingPackage, "getSubscriptionsInGroup")
-                    || (info.isEmbedded() && info.canManageSubscription(mContext, callingPackage));
+                    || info.canManageSubscription(mContext, callingPackage);
         }).collect(Collectors.toList());
     }
 
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 87d48ab..793f846 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -66,8 +66,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -199,13 +197,13 @@
     /**
      * Update subscriptions when given a new ICC state.
      */
-    public void updateInternalIccState(String simStatus, String reason, int slotId,
+    public void updateInternalIccState(String simStatus, String reason, int phoneId,
             boolean absentAndInactive) {
         logd("updateInternalIccState to simStatus " + simStatus + " reason " + reason
-                + " slotId " + slotId);
+                + " phoneId " + phoneId);
         int message = internalIccStateToMessage(simStatus);
         if (message != EVENT_INVALID) {
-            sendMessage(obtainMessage(message, slotId, absentAndInactive ? 1 : 0, reason));
+            sendMessage(obtainMessage(message, phoneId, absentAndInactive ? 1 : 0, reason));
         }
     }
 
@@ -373,15 +371,15 @@
                 EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS, cardId, 0 /* arg2 */, callback));
     }
 
-    private void handleSimLocked(int slotId, String reason) {
-        if (sIccId[slotId] != null && sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
-            logd("SIM" + (slotId + 1) + " hot plug in");
-            sIccId[slotId] = null;
+    private void handleSimLocked(int phoneId, String reason) {
+        if (sIccId[phoneId] != null && sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
+            logd("SIM" + (phoneId + 1) + " hot plug in");
+            sIccId[phoneId] = null;
         }
 
-        String iccId = sIccId[slotId];
+        String iccId = sIccId[phoneId];
         if (iccId == null) {
-            IccCard iccCard = PhoneFactory.getPhone(slotId).getIccCard();
+            IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
             if (iccCard == null) {
                 logd("handleSimLocked: IccCard null");
                 return;
@@ -395,18 +393,18 @@
                 logd("handleSimLocked: IccID null");
                 return;
             }
-            sIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId());
+            sIccId[phoneId] = IccUtils.stripTrailingFs(records.getFullIccId());
         } else {
-            logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId);
+            logd("NOT Querying IccId its already set sIccid[" + phoneId + "]=" + iccId);
         }
 
-        updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);
+        updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
 
-        broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, reason);
-        broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_PRESENT);
-        broadcastSimApplicationStateChanged(slotId, getSimStateFromLockedReason(reason));
-        updateSubscriptionCarrierId(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
-        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
+        broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, reason);
+        broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
+        broadcastSimApplicationStateChanged(phoneId, getSimStateFromLockedReason(reason));
+        updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
+        updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
     }
 
     private static int getSimStateFromLockedReason(String lockedReason) {
@@ -425,32 +423,31 @@
         }
     }
 
-    private void handleSimNotReady(int slotId) {
-        logd("handleSimNotReady: slotId: " + slotId);
+    private void handleSimNotReady(int phoneId) {
+        logd("handleSimNotReady: phoneId: " + phoneId);
 
-        IccCard iccCard = PhoneFactory.getPhone(slotId).getIccCard();
+        IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
         if (iccCard.isEmptyProfile()) {
             // ICC_NOT_READY is a terminal state for an eSIM on the boot profile. 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[slotId] = ICCID_STRING_FOR_NO_SIM;
-            updateSubscriptionInfoByIccId(slotId, false /* updateEmbeddedSubs */);
+            sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
+            updateSubscriptionInfoByIccId(phoneId, false /* updateEmbeddedSubs */);
         }
 
-        broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_NOT_READY,
+        broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_NOT_READY,
                 null);
-        broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_PRESENT);
-        broadcastSimApplicationStateChanged(slotId, TelephonyManager.SIM_STATE_NOT_READY);
+        broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
+        broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY);
     }
 
-    private void handleSimLoaded(int slotId) {
-        logd("handleSimLoaded: slotId: " + slotId);
+    private void handleSimLoaded(int phoneId) {
+        logd("handleSimLoaded: phoneId: " + phoneId);
 
         // The SIM should be loaded at this state, but it is possible in cases such as SIM being
         // removed or a refresh RESET that the IccRecords could be null. The right behavior is to
         // not broadcast the SIM loaded.
-        int loadedSlotId = slotId;
-        IccCard iccCard = PhoneFactory.getPhone(slotId).getIccCard();
+        IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
         if (iccCard == null) {  // Possibly a race condition.
             logd("handleSimLoaded: IccCard null");
             return;
@@ -464,13 +461,13 @@
             logd("handleSimLoaded: IccID null");
             return;
         }
-        sIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId());
+        sIccId[phoneId] = IccUtils.stripTrailingFs(records.getFullIccId());
 
-        updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);
+        updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
         List<SubscriptionInfo> subscriptionInfos = SubscriptionController.getInstance()
-                .getSubInfoUsingSlotIndexPrivileged(slotId);
+                .getSubInfoUsingSlotIndexPrivileged(phoneId);
         if (subscriptionInfos == null || subscriptionInfos.isEmpty()) {
-            loge("empty subinfo for slotId: " + slotId + "could not update ContentResolver");
+            loge("empty subinfo for phoneId: " + phoneId + "could not update ContentResolver");
         } else {
             for (SubscriptionInfo sub : subscriptionInfos) {
                 int subId = sub.getSubscriptionId();
@@ -487,7 +484,7 @@
                     logd("EVENT_RECORDS_LOADED Operator name is null");
                 }
 
-                String iso = tm.getSimCountryIsoForPhone(slotId);
+                String iso = tm.getSimCountryIsoForPhone(phoneId);
 
                 if (!TextUtils.isEmpty(iso)) {
                     SubscriptionController.getInstance().setCountryIso(iso, subId);
@@ -516,11 +513,11 @@
                  */
                 SharedPreferences sp =
                         PreferenceManager.getDefaultSharedPreferences(sContext);
-                int storedSubId = sp.getInt(CURR_SUBID + slotId, -1);
+                int storedSubId = sp.getInt(CURR_SUBID + phoneId, -1);
 
                 if (storedSubId != subId) {
                     int networkType = Settings.Global.getInt(
-                            PhoneFactory.getPhone(slotId).getContext().getContentResolver(),
+                            PhoneFactory.getPhone(phoneId).getContext().getContentResolver(),
                             Settings.Global.PREFERRED_NETWORK_MODE + subId,
                             -1 /* invalid network mode */);
 
@@ -529,28 +526,28 @@
                         try {
                             networkType = TelephonyManager.getIntAtIndex(
                                     sContext.getContentResolver(),
-                                    Settings.Global.PREFERRED_NETWORK_MODE, slotId);
+                                    Settings.Global.PREFERRED_NETWORK_MODE, phoneId);
                         } catch (SettingNotFoundException retrySnfe) {
                             Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for "
                                     + "Settings.Global.PREFERRED_NETWORK_MODE");
                         }
                         Settings.Global.putInt(
-                                PhoneFactory.getPhone(slotId).getContext().getContentResolver(),
+                                PhoneFactory.getPhone(phoneId).getContext().getContentResolver(),
                                 Global.PREFERRED_NETWORK_MODE + subId,
                                 networkType);
                     }
 
                     // Set the modem network mode
-                    PhoneFactory.getPhone(slotId).setPreferredNetworkType(networkType, null);
+                    PhoneFactory.getPhone(phoneId).setPreferredNetworkType(networkType, null);
 
                     // Only support automatic selection mode on SIM change.
-                    PhoneFactory.getPhone(slotId).getNetworkSelectionMode(
+                    PhoneFactory.getPhone(phoneId).getNetworkSelectionMode(
                             obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE,
-                                    new Integer(slotId)));
+                                    new Integer(phoneId)));
 
                     // Update stored subId
                     SharedPreferences.Editor editor = sp.edit();
-                    editor.putInt(CURR_SUBID + slotId, subId);
+                    editor.putInt(CURR_SUBID + phoneId, subId);
                     editor.apply();
                 }
             }
@@ -569,80 +566,80 @@
          *  3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
          *  4. ACTION_CARRIER_CONFIG_CHANGED
          */
-        broadcastSimStateChanged(loadedSlotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
-        broadcastSimCardStateChanged(loadedSlotId, TelephonyManager.SIM_STATE_PRESENT);
-        broadcastSimApplicationStateChanged(loadedSlotId, TelephonyManager.SIM_STATE_LOADED);
-        updateSubscriptionCarrierId(loadedSlotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
-        updateCarrierServices(loadedSlotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        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);
+        updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
     }
 
-    private void updateCarrierServices(int slotId, String simState) {
+    private void updateCarrierServices(int phoneId, String simState) {
         CarrierConfigManager configManager =
                 (CarrierConfigManager) sContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        configManager.updateConfigForPhoneId(slotId, simState);
-        mCarrierServiceBindHelper.updateForPhoneId(slotId, simState);
+        configManager.updateConfigForPhoneId(phoneId, simState);
+        mCarrierServiceBindHelper.updateForPhoneId(phoneId, simState);
     }
 
-    private void updateSubscriptionCarrierId(int slotId, String simState) {
-        if (PhoneFactory.getPhone(slotId) != null) {
-            PhoneFactory.getPhone(slotId).resolveSubscriptionCarrierId(simState);
+    private void updateSubscriptionCarrierId(int phoneId, String simState) {
+        if (PhoneFactory.getPhone(phoneId) != null) {
+            PhoneFactory.getPhone(phoneId).resolveSubscriptionCarrierId(simState);
         }
     }
 
-    private void handleSimAbsent(int slotId, int absentAndInactive) {
-        if (sIccId[slotId] != null && !sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
-            logd("SIM" + (slotId + 1) + " hot plug out, absentAndInactive=" + absentAndInactive);
+    private void handleSimAbsent(int phoneId, int absentAndInactive) {
+        if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
+            logd("SIM" + (phoneId + 1) + " hot plug out, absentAndInactive=" + absentAndInactive);
         }
-        sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
-        updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);
-        // Do not broadcast if the SIM is absent and inactive, because the logical slotId here is
+        sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
+        updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
+        // Do not broadcast if the SIM is absent and inactive, because the logical phoneId here is
         // no longer correct
         if (absentAndInactive == 0) {
-            broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT, null);
-            broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_ABSENT);
-            broadcastSimApplicationStateChanged(slotId, TelephonyManager.SIM_STATE_UNKNOWN);
-            updateSubscriptionCarrierId(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
-            updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+            broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT, null);
+            broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_ABSENT);
+            broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_UNKNOWN);
+            updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+            updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
         }
     }
 
-    private void handleSimError(int slotId) {
-        if (sIccId[slotId] != null && !sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
-            logd("SIM" + (slotId + 1) + " Error ");
+    private void handleSimError(int phoneId) {
+        if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
+            logd("SIM" + (phoneId + 1) + " Error ");
         }
-        sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
-        updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);
-        broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR,
+        sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
+        updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
+        broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR,
                 IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
-        broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_CARD_IO_ERROR);
-        broadcastSimApplicationStateChanged(slotId, TelephonyManager.SIM_STATE_NOT_READY);
-        updateSubscriptionCarrierId(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
-        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+        broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_CARD_IO_ERROR);
+        broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY);
+        updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+        updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
     }
 
-    private synchronized void updateSubscriptionInfoByIccId(int slotIndex,
+    private synchronized void updateSubscriptionInfoByIccId(int phoneId,
             boolean updateEmbeddedSubs) {
-        logd("updateSubscriptionInfoByIccId:+ Start");
-        if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
-            loge("[updateSubscriptionInfoByIccId]- invalid slotIndex=" + slotIndex);
+        logd("updateSubscriptionInfoByIccId:+ Start - phoneId: " + phoneId);
+        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+            loge("[updateSubscriptionInfoByIccId]- invalid phoneId=" + phoneId);
             return;
         }
-        logd("updateSubscriptionInfoByIccId: removing subscription info record: slotIndex "
-                + slotIndex);
-        // Clear slotIndex only when sim absent is not enough. It's possible to switch SIM profile
+        logd("updateSubscriptionInfoByIccId: removing subscription info record: phoneId "
+                + phoneId);
+        // Clear phoneId only when sim absent is not enough. It's possible to switch SIM profile
         // within the same slot. Need to clear the slot index of the previous sub. Thus always clear
         // for the changing slot first.
-        SubscriptionController.getInstance().clearSubInfoRecord(slotIndex);
+        SubscriptionController.getInstance().clearSubInfoRecord(phoneId);
 
         // If SIM is not absent, insert new record or update existing record.
-        if (!ICCID_STRING_FOR_NO_SIM.equals(sIccId[slotIndex])) {
+        if (!ICCID_STRING_FOR_NO_SIM.equals(sIccId[phoneId])) {
             logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: "
-                    + sIccId[slotIndex] + "slot: " + slotIndex);
-            mSubscriptionManager.addSubscriptionInfoRecord(sIccId[slotIndex], slotIndex);
+                    + sIccId[phoneId] + ", phoneId:" + phoneId);
+            mSubscriptionManager.addSubscriptionInfoRecord(sIccId[phoneId], phoneId);
         }
 
         List<SubscriptionInfo> subInfos = SubscriptionController.getInstance()
-                .getSubInfoUsingSlotIndexPrivileged(slotIndex);
+                .getSubInfoUsingSlotIndexPrivileged(phoneId);
         if (subInfos != null) {
             boolean changed = false;
             for (int i = 0; i < subInfos.size(); i++) {
@@ -985,58 +982,55 @@
             return;
         }
 
-        if (!isCarrierServicePackage(phoneId, configPackageName)) {
-            loge("Cannot manage subId=" + currentSubId + ", carrierPackage=" + configPackageName);
-            return;
-        }
-
         ContentValues cv = new ContentValues();
-        boolean isOpportunistic = config.getBoolean(
-                CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, false);
-        if (currentSubInfo.isOpportunistic() != isOpportunistic) {
-            if (DBG) logd("Set SubId=" + currentSubId + " isOpportunistic=" + isOpportunistic);
-            cv.put(SubscriptionManager.IS_OPPORTUNISTIC, isOpportunistic ? "1" : "0");
-        }
+        ParcelUuid groupUuid = null;
 
+        // carrier certificates are not subscription-specific, so we want to load them even if
+        // this current package is not a CarrierServicePackage
         String[] certs = config.getStringArray(
             CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
         if (certs != null) {
             UiccAccessRule[] carrierConfigAccessRules = new UiccAccessRule[certs.length];
-            try {
-                for (int i = 0; i < certs.length; i++) {
-                    carrierConfigAccessRules[i] = new UiccAccessRule(
-                        MessageDigest.getInstance("SHA-256").digest(certs[i].getBytes()), null, 0);
-                }
-            } catch (NoSuchAlgorithmException e) {
-                throw new RuntimeException("for setCarrierConfigAccessRules, SHA-256 must exist",
-                    e);
+            for (int i = 0; i < certs.length; i++) {
+                carrierConfigAccessRules[i] = new UiccAccessRule(IccUtils.hexStringToBytes(
+                    certs[i]), null, 0);
             }
             cv.put(SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS,
                     UiccAccessRule.encodeRules(carrierConfigAccessRules));
         }
 
-        String groupUuidString =
+        if (!isCarrierServicePackage(phoneId, configPackageName)) {
+            loge("Cannot manage subId=" + currentSubId + ", carrierPackage=" + configPackageName);
+        } else {
+            boolean isOpportunistic = config.getBoolean(
+                    CarrierConfigManager.KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, false);
+            if (currentSubInfo.isOpportunistic() != isOpportunistic) {
+                if (DBG) logd("Set SubId=" + currentSubId + " isOpportunistic=" + isOpportunistic);
+                cv.put(SubscriptionManager.IS_OPPORTUNISTIC, isOpportunistic ? "1" : "0");
+            }
+
+            String groupUuidString =
                 config.getString(CarrierConfigManager.KEY_SUBSCRIPTION_GROUP_UUID_STRING, "");
-        ParcelUuid groupUuid = null;
-        if (!TextUtils.isEmpty(groupUuidString)) {
-            try {
-                // Update via a UUID Structure to ensure consistent formatting
-                groupUuid = ParcelUuid.fromString(groupUuidString);
-                if (groupUuid.equals(REMOVE_GROUP_UUID)
+            if (!TextUtils.isEmpty(groupUuidString)) {
+                try {
+                    // Update via a UUID Structure to ensure consistent formatting
+                    groupUuid = ParcelUuid.fromString(groupUuidString);
+                    if (groupUuid.equals(REMOVE_GROUP_UUID)
                             && currentSubInfo.getGroupUuid() != null) {
-                    cv.put(SubscriptionManager.GROUP_UUID, (String) null);
-                    if (DBG) logd("Group Removed for" + currentSubId);
-                } else if (SubscriptionController.getInstance().canPackageManageGroup(groupUuid,
+                        cv.put(SubscriptionManager.GROUP_UUID, (String) null);
+                        if (DBG) logd("Group Removed for" + currentSubId);
+                    } else if (SubscriptionController.getInstance().canPackageManageGroup(groupUuid,
                         configPackageName)) {
-                    cv.put(SubscriptionManager.GROUP_UUID, groupUuid.toString());
-                    cv.put(SubscriptionManager.GROUP_OWNER, configPackageName);
-                    if (DBG) logd("Group Added for" + currentSubId);
-                } else {
-                    loge("configPackageName " + configPackageName + " doesn't own grouUuid "
+                        cv.put(SubscriptionManager.GROUP_UUID, groupUuid.toString());
+                        cv.put(SubscriptionManager.GROUP_OWNER, configPackageName);
+                        if (DBG) logd("Group Added for" + currentSubId);
+                    } else {
+                        loge("configPackageName " + configPackageName + " doesn't own grouUuid "
                             + groupUuid);
+                    }
+                } catch (IllegalArgumentException e) {
+                    loge("Invalid Group UUID=" + groupUuidString);
                 }
-            } catch (IllegalArgumentException e) {
-                loge("Invalid Group UUID=" + groupUuidString);
             }
         }
         if (cv.size() > 0 && sContext.getContentResolver().update(SubscriptionManager
@@ -1073,7 +1067,7 @@
     }
 
     @UnsupportedAppUsage
-    private void broadcastSimStateChanged(int slotId, String state, String reason) {
+    private void broadcastSimStateChanged(int phoneId, String state, String reason) {
         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
@@ -1084,10 +1078,10 @@
         i.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
         i.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, state);
         i.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
-        SubscriptionManager.putPhoneIdAndSubIdExtra(i, slotId);
+        SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId);
         logd("Broadcasting intent ACTION_SIM_STATE_CHANGED " + state + " reason " + reason +
-                " for phone: " + slotId);
-        IntentBroadcaster.getInstance().broadcastStickyIntent(i, slotId);
+                " for phone: " + phoneId);
+        IntentBroadcaster.getInstance().broadcastStickyIntent(i, phoneId);
     }
 
     private void broadcastSimCardStateChanged(int phoneId, int state) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index f98fc40..c7dbf4e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -483,6 +483,9 @@
                 }
                 if (mImsCall != null) mImsCall.close();
                 mImsCall = null;
+                if (mImsVideoCallProviderWrapper != null) {
+                    mImsVideoCallProviderWrapper.tearDown();
+                }
             }
         }
         releaseWakeLock();
diff --git a/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java b/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java
index edc3e67..5b41d8e 100644
--- a/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java
+++ b/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java
@@ -100,8 +100,6 @@
 
     // Miscellaneous dependencies and helpers not related to detection state.
     private final int mPhoneId;
-    /** Accesses global information about the device. */
-    private final DeviceState mDeviceState;
     /** Applied to NITZ signals during input filtering. */
     private final NitzSignalInputFilterPredicate mNitzSignalInputFilter;
     /** Creates {@link PhoneTimeZoneSuggestion} for passing to the time zone detection service. */
@@ -122,13 +120,11 @@
     // Time Zone detection state.
 
     /**
-     * Records whether the device should have a country code available via
-     * {@link DeviceState#getNetworkCountryIsoForPhone()}. Before this an NITZ signal
-     * received is (almost always) not enough to determine time zone. On test networks the country
-     * code should be available but can still be an empty string but this flag indicates that the
-     * information available is unlikely to improve.
+     * Records the country to use for time zone detection. It can be a valid ISO 3166 alpha-2 code
+     * (lower case), empty (test network) or null (no country detected). A country code is required
+     * to determine time zone except when on a test network.
      */
-    private boolean mGotCountryCode = false;
+    private String mCountryIsoCode;
 
     /**
      * Creates an instance for the supplied {@link Phone}.
@@ -145,7 +141,7 @@
         NitzSignalInputFilterPredicate nitzSignalFilter =
                 NitzSignalInputFilterPredicateFactory.create(phone.getContext(), deviceState);
         return new NewNitzStateMachineImpl(
-                phoneId, nitzSignalFilter, timeZoneSuggester, newTimeServiceHelper, deviceState);
+                phoneId, nitzSignalFilter, timeZoneSuggester, newTimeServiceHelper);
     }
 
     /**
@@ -156,29 +152,35 @@
     public NewNitzStateMachineImpl(int phoneId,
             @NonNull NitzSignalInputFilterPredicate nitzSignalInputFilter,
             @NonNull TimeZoneSuggester timeZoneSuggester,
-            @NonNull NewTimeServiceHelper newTimeServiceHelper, @NonNull DeviceState deviceState) {
+            @NonNull NewTimeServiceHelper newTimeServiceHelper) {
         mPhoneId = phoneId;
         mTimeZoneSuggester = Objects.requireNonNull(timeZoneSuggester);
         mNewTimeServiceHelper = Objects.requireNonNull(newTimeServiceHelper);
-        mDeviceState = Objects.requireNonNull(deviceState);
         mNitzSignalInputFilter = Objects.requireNonNull(nitzSignalInputFilter);
     }
 
     @Override
     public void handleNetworkAvailable() {
+        // We no longer do any useful work here: we assume handleNetworkUnavailable() is reliable.
+        // TODO: Remove this method when all implementations do nothing.
+    }
+
+    @Override
+    public void handleNetworkUnavailable() {
+        String reason = "handleNetworkUnavailable()";
+        clearNetworkStateAndRerunDetection(reason);
+    }
+
+    private void clearNetworkStateAndRerunDetection(String reason) {
         // Assume any previous NITZ signals received are now invalid.
         mLatestNitzSignal = null;
 
-        String countryIsoCode =
-                mGotCountryCode ? mDeviceState.getNetworkCountryIsoForPhone() : null;
+        String countryIsoCode = mCountryIsoCode;
 
         if (DBG) {
-            Rlog.d(LOG_TAG, "handleNetworkAvailable: countryIsoCode=" + countryIsoCode
-                    + ", mLatestNitzSignal=" + mLatestNitzSignal);
+            Rlog.d(LOG_TAG, reason + ": countryIsoCode=" + countryIsoCode);
         }
 
-        String reason = "handleNetworkAvailable()";
-
         // Generate a new time zone suggestion and update the service as needed.
         doTimeZoneDetection(countryIsoCode, null /* nitzSignal */, reason);
 
@@ -187,31 +189,32 @@
     }
 
     @Override
-    public void handleNetworkCountryCodeSet(boolean countryChanged) {
+    public void handleCountryDetected(@NonNull String countryIsoCode) {
         if (DBG) {
-            Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: countryChanged=" + countryChanged
+            Rlog.d(LOG_TAG, "handleCountryDetected: countryIsoCode=" + countryIsoCode
                     + ", mLatestNitzSignal=" + mLatestNitzSignal);
         }
 
-        mGotCountryCode = true;
-
-        // Generate a new time zone suggestion and update the service as needed.
-        String countryIsoCode = mDeviceState.getNetworkCountryIsoForPhone();
-        doTimeZoneDetection(countryIsoCode, mLatestNitzSignal,
-                "handleNetworkCountryCodeSet(" + countryChanged + ")");
+        String oldCountryIsoCode = mCountryIsoCode;
+        mCountryIsoCode = Objects.requireNonNull(countryIsoCode);
+        if (!Objects.equals(oldCountryIsoCode, mCountryIsoCode)) {
+            // Generate a new time zone suggestion and update the service as needed.
+            doTimeZoneDetection(countryIsoCode, mLatestNitzSignal,
+                    "handleCountryDetected(\"" + countryIsoCode + "\")");
+        }
     }
 
     @Override
-    public void handleNetworkCountryCodeUnavailable() {
+    public void handleCountryUnavailable() {
         if (DBG) {
-            Rlog.d(LOG_TAG, "handleNetworkCountryCodeUnavailable:"
+            Rlog.d(LOG_TAG, "handleCountryUnavailable:"
                     + " mLatestNitzSignal=" + mLatestNitzSignal);
         }
-        mGotCountryCode = false;
+        mCountryIsoCode = null;
 
         // Generate a new time zone suggestion and update the service as needed.
         doTimeZoneDetection(null /* countryIsoCode */, mLatestNitzSignal,
-                "handleNetworkCountryCodeUnavailable()");
+                "handleCountryUnavailable()");
     }
 
     @Override
@@ -233,8 +236,7 @@
         String reason = "handleNitzReceived(" + nitzSignal + ")";
 
         // Generate a new time zone suggestion and update the service as needed.
-        String countryIsoCode =
-                mGotCountryCode ? mDeviceState.getNetworkCountryIsoForPhone() : null;
+        String countryIsoCode = mCountryIsoCode;
         doTimeZoneDetection(countryIsoCode, nitzSignal, reason);
 
         // Generate a new time suggestion and update the service as needed.
@@ -243,10 +245,6 @@
 
     @Override
     public void handleAirplaneModeChanged(boolean on) {
-        if (DBG) {
-            Rlog.d(LOG_TAG, "handleAirplaneModeChanged: on=" + on);
-        }
-
         // Treat entry / exit from airplane mode as a strong signal that the user wants to clear
         // cached state. If the user really is boarding a plane they won't want cached state from
         // before their flight influencing behavior.
@@ -260,20 +258,11 @@
         // will be made after airplane mode is re-enabled as the device re-establishes network
         // connectivity.
 
-        // Clear shared state.
-        mLatestNitzSignal = null;
-
         // Clear time zone detection state.
-        mGotCountryCode = false;
+        mCountryIsoCode = null;
 
         String reason = "handleAirplaneModeChanged(" + on + ")";
-
-        // Generate a new time zone suggestion and update the service as needed.
-        doTimeZoneDetection(null /* countryIsoCode */, null /* nitzSignal */,
-                reason);
-
-        // Generate a new time suggestion and update the service as needed.
-        doTimeDetection(null /* nitzSignal */, reason);
+        clearNetworkStateAndRerunDetection(reason);
     }
 
     /**
@@ -340,7 +329,7 @@
     @Override
     public void dumpState(PrintWriter pw) {
         pw.println(" NewNitzStateMachineImpl.mLatestNitzSignal=" + mLatestNitzSignal);
-        pw.println(" NewNitzStateMachineImpl.mGotCountryCode=" + mGotCountryCode);
+        pw.println(" NewNitzStateMachineImpl.mCountryIsoCode=" + mCountryIsoCode);
         mNewTimeServiceHelper.dumpState(pw);
         pw.flush();
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
index 51aa5d0..5d69ab6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
@@ -20,7 +20,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
@@ -55,7 +54,7 @@
 
     private static final String US_MCC = "310";
     private static final String LIECHTENSTEIN_MCC = "295";
-    private static final String BOGUS_MCC = "001";
+    private static final String TEST_CELL_MCC = "001";
 
     private static final String FAKE_MNC = "123";
 
@@ -284,14 +283,14 @@
             throws Exception {
         mLocaleTracker.updateOperatorNumeric("");
         sendOperatorLost();
-        verify(mNitzStateMachine, times(1)).handleNetworkCountryCodeUnavailable();
+        verify(mNitzStateMachine, times(1)).handleCountryUnavailable();
     }
 
     @Test
     @SmallTest
-    public void updateOperatorNumeric_BogusNetwork_shouldHandleNetworkCountryCodeSet()
+    public void updateOperatorNumeric_TestNetwork_shouldHandleNetworkCountryCodeSet()
             throws Exception {
-        mLocaleTracker.updateOperatorNumeric(BOGUS_MCC + FAKE_MNC);
-        verify(mNitzStateMachine, times(1)).handleNetworkCountryCodeSet(anyBoolean());
+        mLocaleTracker.updateOperatorNumeric(TEST_CELL_MCC + FAKE_MNC);
+        verify(mNitzStateMachine, times(1)).handleCountryDetected("");
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineImplTest.java b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineImplTest.java
index 4248c84..3702d3e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineImplTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineImplTest.java
@@ -83,7 +83,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> nitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -149,7 +150,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(null /* uninitialized */);
+                .initializeTimeZoneSetting(null /* uninitialized */)
+                .networkAvailable();
 
         script.nitzReceived(nitzSignal);
         PhoneTimeSuggestion expectedTimeSuggestion =
@@ -181,7 +183,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         script.nitzReceived(nitzSignal);
         PhoneTimeSuggestion expectedTimeSuggestion =
@@ -199,7 +202,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> nitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -224,7 +228,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         // Demonstrate the defaultTimeZoneBoost behavior: we can get a zone only from the
         // countryIsoCode.
@@ -288,7 +293,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         // Demonstrate the behavior without default country boost for a country with multiple zones:
         // we cannot get a zone only from the countryIsoCode.
@@ -351,7 +357,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(false)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> nitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -375,7 +382,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(false)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> nitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -399,7 +407,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         // Simulate receiving an NITZ signal.
         TimestampedValue<NitzData> nitzSignal =
@@ -431,7 +440,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         // Simulate receiving an NITZ signal.
         TimestampedValue<NitzData> nitzSignal =
@@ -463,7 +473,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> goodNitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -495,7 +506,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> goodNitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -530,7 +542,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> goodNitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -571,7 +584,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> goodNitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -612,7 +626,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> goodNitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -654,7 +669,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> goodNitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -696,7 +712,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         TimestampedValue<NitzData> originalNitzSignal =
                 scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
@@ -731,7 +748,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
 
@@ -762,7 +780,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
 
@@ -796,7 +815,8 @@
         Script script = new Script()
                 .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
                 .initializeTimeZoneDetectionEnabled(true)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .networkAvailable();
 
         // Pre-flight: Simulate a device receiving signals that allow it to detect time and time
         // zone.
@@ -887,6 +907,193 @@
     }
 
     /**
+     * Confirm losing the network / NITZ doesn't clear country state.
+     */
+    @Test
+    public void test_handleNetworkUnavailableUs() throws Exception {
+        // Use the US in this test: it requires country + NITZ to detect a time zone.
+        Scenario scenario = UNIQUE_US_ZONE_SCENARIO1.mutableCopy();
+        int timeStepMillis = (int) TimeUnit.HOURS.toMillis(3);
+
+        Script script = new Script()
+                .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+                .initializeTimeZoneDetectionEnabled(true)
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+        // Simulate a device receiving signals that allow it to detect time and time zone.
+        TimestampedValue<NitzData> initialNitzSignal =
+                scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+        script.networkAvailable()
+                .nitzReceived(initialNitzSignal)
+                .countryReceived(scenario.getNetworkCountryIsoCode());
+
+        PhoneTimeSuggestion expectedInitialTimeSuggestion =
+                createTimeSuggestionFromNitzSignal(mPhone.getPhoneId(), initialNitzSignal);
+        script.verifyTimeSuggestedAndZoneSetAndReset(
+                expectedInitialTimeSuggestion, scenario.getTimeZoneId());
+
+        // Demonstrate the NitzStateMachineImpl is "opinionated" about time zone: toggling auto-time
+        // zone on should cause it to set the last known time zone again.
+        // Note: Historically Android telephony time detection hasn't retained an opinion about time
+        // so only the time zone is set. Also, NitzStateMachine doesn't pay attention to whether
+        // auto-time is enabled; it is left to the system server service to decide whether to act on
+        // the time suggestion if the settings allow.
+        script.toggleTimeZoneDetectionEnabled(false)
+                .verifyNothingWasSetAndReset()
+                .toggleTimeZoneDetectionEnabled(true)
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+        // Check state that NitzStateMachine must expose.
+        assertEquals(initialNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+
+        // Simulate losing the network. The NitzStateMachineImpl must lose all NITZ state and stop
+        // having an opinion about time zone.
+        script.networkUnavailable()
+                .verifyNothingWasSetAndReset();
+
+        // Simulate the passage of time and update the device realtime clock.
+        scenario.incrementTime(timeStepMillis);
+        script.incrementTime(timeStepMillis);
+
+        // Check state that NitzStateMachine must expose.
+        assertNull(mNitzStateMachine.getCachedNitzData());
+
+        // Verify there's no time zone opinion by toggling auto time zone off and on.
+        script.toggleTimeZoneDetectionEnabled(false)
+                .verifyNothingWasSetAndReset()
+                .toggleTimeZoneDetectionEnabled(true)
+                .verifyNothingWasSetAndReset();
+
+        // Simulate the passage of time and update the device realtime clock.
+        scenario.incrementTime(timeStepMillis);
+        script.incrementTime(timeStepMillis);
+
+        // Simulate the network becoming available again (but no NITZ yet).
+        script.networkAvailable();
+
+        // Verify there's still no opinion by toggling auto time zone off and on.
+        script.toggleTimeZoneDetectionEnabled(false)
+                .verifyNothingWasSetAndReset()
+                .toggleTimeZoneDetectionEnabled(true)
+                .verifyNothingWasSetAndReset();
+
+        // Check the state that NitzStateMachine must expose.
+        assertNull(mNitzStateMachine.getCachedNitzData());
+
+        // Simulate the passage of time and update the device realtime clock.
+        scenario.incrementTime(timeStepMillis);
+        script.incrementTime(timeStepMillis);
+
+        // Simulate the device receiving NITZ signals again now the network is available. Now the
+        // NitzStateMachineImpl is opinionated again.
+        TimestampedValue<NitzData> finalNitzSignal =
+                scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+        script.nitzReceived(finalNitzSignal);
+
+        PhoneTimeSuggestion expectedFinalTimeSuggestion =
+                createTimeSuggestionFromNitzSignal(mPhone.getPhoneId(), finalNitzSignal);
+        script.verifyTimeSuggestedAndZoneSetAndReset(expectedFinalTimeSuggestion,
+                        scenario.getTimeZoneId());
+
+        // Check state that NitzStateMachine must expose.
+        assertEquals(finalNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+    }
+
+    /**
+     * Confirm losing the network / NITZ doesn't clear country state.
+     */
+    @Test
+    public void test_handleNetworkUnavailableUk() throws Exception {
+        // Use the UK in this test: it only requires country to detect a time zone.
+        Scenario scenario = UNITED_KINGDOM_SCENARIO.mutableCopy();
+        int timeStepMillis = (int) TimeUnit.HOURS.toMillis(3);
+
+        Script script = new Script()
+                .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+                .initializeTimeZoneDetectionEnabled(true)
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+        // Simulate a device receiving signals that allow it to detect time and time zone.
+        TimestampedValue<NitzData> initialNitzSignal =
+                scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+        script.networkAvailable()
+                .nitzReceived(initialNitzSignal)
+                .countryReceived(scenario.getNetworkCountryIsoCode());
+
+        PhoneTimeSuggestion expectedInitialTimeSuggestion =
+                createTimeSuggestionFromNitzSignal(mPhone.getPhoneId(), initialNitzSignal);
+        script.verifyTimeSuggestedAndZoneSetAndReset(
+                expectedInitialTimeSuggestion, scenario.getTimeZoneId());
+
+        // Demonstrate the NitzStateMachineImpl is "opinionated" about time zone: toggling auto-time
+        // zone on should cause it to set the last known time zone again.
+        // Note: Historically Android telephony time detection hasn't retained an opinion about time
+        // so only the time zone is set. Also, NitzStateMachine doesn't pay attention to whether
+        // auto-time is enabled; it is left to the system server service to decide whether to act on
+        // the time suggestion if the settings allow.
+        script.toggleTimeZoneDetectionEnabled(false)
+                .verifyNothingWasSetAndReset()
+                .toggleTimeZoneDetectionEnabled(true)
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+        // Check state that NitzStateMachine must expose.
+        assertEquals(initialNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+
+        // Simulate losing the network. The NitzStateMachineImpl must lose all NITZ state but should
+        // retain country knowledge and so remain opinionated about time zone ID because the country
+        // is sufficient to detect time zone in the UK.
+        script.networkUnavailable()
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+        // Simulate the passage of time and update the device realtime clock.
+        scenario.incrementTime(timeStepMillis);
+        script.incrementTime(timeStepMillis);
+
+        // Check state that NitzStateMachine must expose.
+        assertNull(mNitzStateMachine.getCachedNitzData());
+
+        // Verify there's a time zone opinion by toggling auto time zone off and on.
+        script.toggleTimeZoneDetectionEnabled(false)
+                .verifyNothingWasSetAndReset()
+                .toggleTimeZoneDetectionEnabled(true)
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+        // Simulate the passage of time and update the device realtime clock.
+        scenario.incrementTime(timeStepMillis);
+        script.incrementTime(timeStepMillis);
+
+        // Simulate the network becoming available again (but no NITZ yet).
+        script.networkAvailable();
+
+        // Verify there's still no opinion by toggling auto time zone off and on.
+        script.toggleTimeZoneDetectionEnabled(false)
+                .verifyNothingWasSetAndReset()
+                .toggleTimeZoneDetectionEnabled(true)
+                .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+        // Check the state that NitzStateMachine must expose.
+        assertNull(mNitzStateMachine.getCachedNitzData());
+
+        // Simulate the passage of time and update the device realtime clock.
+        scenario.incrementTime(timeStepMillis);
+        script.incrementTime(timeStepMillis);
+
+        // Simulate the device receiving NITZ signals again now the network is available. Now the
+        // NitzStateMachineImpl is opinionated again.
+        TimestampedValue<NitzData> finalNitzSignal =
+                scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+        script.nitzReceived(finalNitzSignal);
+
+        PhoneTimeSuggestion expectedFinalTimeSuggestion =
+                createTimeSuggestionFromNitzSignal(mPhone.getPhoneId(), finalNitzSignal);
+        script.verifyTimeSuggestedAndZoneSetAndReset(expectedFinalTimeSuggestion,
+                        scenario.getTimeZoneId());
+
+        // Check state that NitzStateMachine must expose.
+        assertEquals(finalNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+    }
+
+    /**
      * Asserts a test scenario has the properties we expect for NITZ-only lookup. There are
      * usually multiple zones that will share the same UTC offset so we get a low quality / low
      * confidence answer, but the zone we find should at least have the correct offset.
@@ -939,8 +1146,12 @@
         }
 
         Script countryReceived(String countryIsoCode) {
-            mFakeDeviceState.networkCountryIsoForPhone = countryIsoCode;
-            mNitzStateMachine.handleNetworkCountryCodeSet(true);
+            mNitzStateMachine.handleCountryDetected(countryIsoCode);
+            return this;
+        }
+
+        Script networkAvailable() {
+            mNitzStateMachine.handleNetworkAvailable();
             return this;
         }
 
@@ -949,6 +1160,11 @@
             return this;
         }
 
+        Script networkUnavailable() {
+            mNitzStateMachine.handleNetworkUnavailable();
+            return this;
+        }
+
         Script toggleAirplaneMode(boolean on) {
             mNitzStateMachine.handleAirplaneModeChanged(on);
             return this;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java
index 02a0cc9..6621c03 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java
@@ -214,7 +214,6 @@
         public boolean ignoreNitz;
         public int nitzUpdateDiffMillis;
         public int nitzUpdateSpacingMillis;
-        public String networkCountryIsoForPhone;
         public long elapsedRealtime;
         public long currentTimeMillis;
 
@@ -242,11 +241,6 @@
         }
 
         @Override
-        public String getNetworkCountryIsoForPhone() {
-            return networkCountryIsoForPhone;
-        }
-
-        @Override
         public long elapsedRealtime() {
             return elapsedRealtime;
         }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index 62ffa6d..6c34903 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -56,6 +56,7 @@
 import com.android.internal.telephony.euicc.EuiccController;
 import com.android.internal.telephony.uicc.IccFileHandler;
 import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.telephony.uicc.UiccSlot;
 
 import org.junit.After;
@@ -67,8 +68,6 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -746,17 +745,13 @@
     @SmallTest
     public void testUpdateFromCarrierConfigCarrierCertificates() {
         String[] certs = new String[2];
-        certs[0] = "testCertificate";
-        certs[1] = "testCertificate2";
+        certs[0] = "d1f1";
+        certs[1] = "b5d6";
 
         UiccAccessRule[] carrierConfigAccessRules = new UiccAccessRule[certs.length];
-        try {
-            for (int i = 0; i < certs.length; i++) {
-                carrierConfigAccessRules[i] = new UiccAccessRule(
-                    MessageDigest.getInstance("SHA-256").digest(certs[i].getBytes()), null, 0);
-            }
-        } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("for setCarrierConfigAccessRules, SHA-256 must exist", e);
+        for (int i = 0; i < certs.length; i++) {
+            carrierConfigAccessRules[i] = new UiccAccessRule(
+                IccUtils.hexStringToBytes(certs[i]), null, 0);
         }
 
         final int phoneId = mPhone.getPhoneId();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/NewNitzStateMachineImplTest.java b/tests/telephonytests/src/com/android/internal/telephony/nitz/NewNitzStateMachineImplTest.java
index d422687..01efb40 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/nitz/NewNitzStateMachineImplTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/NewNitzStateMachineImplTest.java
@@ -85,7 +85,7 @@
 
         mNitzStateMachineImpl = new NewNitzStateMachineImpl(
                 PHONE_ID, mFakeNitzSignalInputFilter, mRealTimeZoneSuggester,
-                mFakeNewTimeServiceHelper, mFakeDeviceState);
+                mFakeNewTimeServiceHelper);
 
         TelephonyTest.logd("NewNitzStateMachineImplTest -Setup!");
     }
@@ -306,7 +306,7 @@
         script.toggleAirplaneMode(true);
 
         // Verify the state machine did the right thing.
-        // Check the time and time zone suggestion was withdrawn.
+        // Check the time zone suggestion was withdrawn (time is not currently withdrawn).
         script.verifyOnlyTimeZoneWasSuggestedAndReset(EMPTY_TIME_ZONE_SUGGESTION);
 
         // Check state that NitzStateMachine must expose.
@@ -359,6 +359,90 @@
         assertEquals(postFlightNitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
     }
 
+    /**
+     * Confirm losing the network / NITZ doesn't clear country state.
+     */
+    @Test
+    public void test_handleNetworkUnavailableClearsNetworkState() throws Exception {
+        Scenario scenario = UNIQUE_US_ZONE_SCENARIO1.mutableCopy();
+        int timeStepMillis = (int) TimeUnit.HOURS.toMillis(3);
+        String countryIsoCode = scenario.getNetworkCountryIsoCode();
+
+        Script script = new Script()
+                .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+                .networkAvailable();
+
+        // Simulate a device receiving signals that allow it to detect time and time zone.
+        TimestampedValue<NitzData> initialNitzSignal =
+                scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+        PhoneTimeSuggestion expectedInitialTimeSuggestion =
+                createTimeSuggestionFromNitzSignal(PHONE_ID, initialNitzSignal);
+
+        // Simulate receiving the NITZ signal and country.
+        script.nitzReceived(initialNitzSignal)
+                .countryReceived(countryIsoCode);
+
+        // Verify the state machine did the right thing.
+        PhoneTimeZoneSuggestion expectedInitialTimeZoneSuggestion =
+                mRealTimeZoneSuggester.getTimeZoneSuggestion(
+                        PHONE_ID, countryIsoCode, initialNitzSignal);
+        script.verifyTimeAndTimeZoneSuggestedAndReset(
+                expectedInitialTimeSuggestion, expectedInitialTimeZoneSuggestion);
+
+        // Check state that NitzStateMachine must expose.
+        assertEquals(initialNitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+
+        // Simulate the passage of time and update the device realtime clock.
+        scenario.incrementTime(timeStepMillis);
+        script.incrementTime(timeStepMillis);
+
+        // Simulate network being lost.
+        script.networkUnavailable();
+
+        // Verify the state machine did the right thing.
+        // Check the "no NITZ" time zone suggestion is made (time is not currently withdrawn).
+        PhoneTimeZoneSuggestion expectedMiddleTimeZoneSuggestion =
+                mRealTimeZoneSuggester.getTimeZoneSuggestion(
+                        PHONE_ID, countryIsoCode, null /* nitzSignal */);
+        script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedMiddleTimeZoneSuggestion);
+
+        // Check state that NitzStateMachine must expose.
+        assertNull(mNitzStateMachineImpl.getCachedNitzData());
+
+        // Simulate the passage of time and update the device realtime clock.
+        scenario.incrementTime(timeStepMillis);
+        script.incrementTime(timeStepMillis);
+
+        // Simulate the network being found.
+        script.networkAvailable()
+                .verifyNothingWasSuggested();
+
+        // Check the state that NitzStateMachine must expose.
+        assertNull(mNitzStateMachineImpl.getCachedNitzData());
+
+        // Simulate the passage of time and update the device realtime clock.
+        scenario.incrementTime(timeStepMillis);
+        script.incrementTime(timeStepMillis);
+
+        // Simulate the device receiving NITZ signal again. Now the NitzStateMachine should be
+        // opinionated again.
+        TimestampedValue<NitzData> finalNitzSignal =
+                scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+        script.nitzReceived(finalNitzSignal);
+
+        // Verify the state machine did the right thing.
+        PhoneTimeSuggestion expectedFinalTimeSuggestion =
+                createTimeSuggestionFromNitzSignal(PHONE_ID, finalNitzSignal);
+        PhoneTimeZoneSuggestion expectedFinalTimeZoneSuggestion =
+                mRealTimeZoneSuggester.getTimeZoneSuggestion(
+                        PHONE_ID, countryIsoCode, finalNitzSignal);
+        script.verifyTimeAndTimeZoneSuggestedAndReset(
+                expectedFinalTimeSuggestion, expectedFinalTimeZoneSuggestion);
+
+        // Check state that NitzStateMachine must expose.
+        assertEquals(finalNitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+    }
+
     @Test
     public void test_countryUnavailableClearsTimeZoneSuggestion() throws Exception {
         Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
@@ -401,47 +485,6 @@
         assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
     }
 
-    @Test
-    public void test_networkAvailableClearsCacheNitzAndTimeZoneSuggestion() throws Exception {
-        Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
-        TimestampedValue<NitzData> nitzSignal =
-                scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
-
-        Script script = new Script()
-                .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
-                .networkAvailable();
-
-        // Simulate receiving the country and verify the state machine does the right thing.
-        script.countryReceived(scenario.getNetworkCountryIsoCode());
-        PhoneTimeZoneSuggestion expectedTimeZoneSuggestion1 =
-                mRealTimeZoneSuggester.getTimeZoneSuggestion(
-                        PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
-        script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion1);
-
-        // Simulate receiving an NITZ signal and verify the state machine does the right thing.
-        script.nitzReceived(nitzSignal);
-        PhoneTimeSuggestion expectedTimeSuggestion =
-                createTimeSuggestionFromNitzSignal(PHONE_ID, nitzSignal);
-        PhoneTimeZoneSuggestion expectedTimeZoneSuggestion2 =
-                mRealTimeZoneSuggester.getTimeZoneSuggestion(
-                        PHONE_ID, scenario.getNetworkCountryIsoCode(), nitzSignal);
-        script.verifyTimeAndTimeZoneSuggestedAndReset(
-                expectedTimeSuggestion, expectedTimeZoneSuggestion2);
-
-        // Check state that NitzStateMachine must expose.
-        assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
-
-        // Simulate network becoming available and verify the state machine does the right thing.
-        script.networkAvailable();
-        PhoneTimeZoneSuggestion expectedTimeZoneSuggestion3 =
-                mRealTimeZoneSuggester.getTimeZoneSuggestion(
-                        PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
-        script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion3);
-
-        // Check state that NitzStateMachine must expose.
-        assertNull(mNitzStateMachineImpl.getCachedNitzData());
-    }
-
     /**
      * A "fluent" helper class allowing reuse of logic for test state initialization, simulation of
      * events, and verification of device state changes with self-describing method names.
@@ -453,8 +496,6 @@
             mFakeDeviceState.ignoreNitz = false;
             mFakeDeviceState.nitzUpdateDiffMillis = 2000;
             mFakeDeviceState.nitzUpdateSpacingMillis = 1000 * 60 * 10;
-
-            mFakeDeviceState.networkCountryIsoForPhone = "";
         }
 
         // Initialization methods for setting simulated device state, usually before simulation.
@@ -476,19 +517,23 @@
             return this;
         }
 
+        Script nitzReceived(TimestampedValue<NitzData> nitzSignal) {
+            mNitzStateMachineImpl.handleNitzReceived(nitzSignal);
+            return this;
+        }
+
+        Script networkUnavailable() {
+            mNitzStateMachineImpl.handleNetworkUnavailable();
+            return this;
+        }
+
         Script countryUnavailable() {
-            mNitzStateMachineImpl.handleNetworkCountryCodeUnavailable();
+            mNitzStateMachineImpl.handleCountryUnavailable();
             return this;
         }
 
         Script countryReceived(String countryIsoCode) {
-            mFakeDeviceState.networkCountryIsoForPhone = countryIsoCode;
-            mNitzStateMachineImpl.handleNetworkCountryCodeSet(true);
-            return this;
-        }
-
-        Script nitzReceived(TimestampedValue<NitzData> nitzSignal) {
-            mNitzStateMachineImpl.handleNitzReceived(nitzSignal);
+            mNitzStateMachineImpl.handleCountryDetected(countryIsoCode);
             return this;
         }
 
@@ -499,6 +544,12 @@
 
         // Verification methods.
 
+        Script verifyNothingWasSuggested() {
+            justVerifyTimeWasNotSuggested();
+            justVerifyTimeWasNotSuggested();
+            return this;
+        }
+
         Script verifyOnlyTimeZoneWasSuggestedAndReset(PhoneTimeZoneSuggestion timeZoneSuggestion) {
             justVerifyTimeZoneWasSuggested(timeZoneSuggestion);
             justVerifyTimeWasNotSuggested();