Merge "Update subscription before broadcasting carrier config changed event"
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index d9505a5..e55fa8e 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -577,7 +577,7 @@
     <string name="dialerKeyboardHintText" msgid="1115266533703764049">"Brug tastatur til at ringe op"</string>
     <string name="onscreenHoldText" msgid="4025348842151665191">"Hold"</string>
     <string name="onscreenEndCallText" msgid="6138725377654842757">"Afslut"</string>
-    <string name="onscreenShowDialpadText" msgid="658465753816164079">"Nummertaster"</string>
+    <string name="onscreenShowDialpadText" msgid="658465753816164079">"Opkaldstastatur"</string>
     <string name="onscreenMuteText" msgid="5470306116733843621">"Lyd fra"</string>
     <string name="onscreenAddCallText" msgid="9075675082903611677">"Tilføj opkald"</string>
     <string name="onscreenMergeCallsText" msgid="3692389519611225407">"Slå opkald sammen"</string>
@@ -657,8 +657,8 @@
     <string name="selectContact" msgid="1527612842599767382">"vælg kontakt"</string>
     <string name="not_voice_capable" msgid="2819996734252084253">"Taleopkald understøttes ikke"</string>
     <string name="description_dial_button" msgid="8614631902795087259">"ring op"</string>
-    <string name="description_dialpad_button" msgid="7395114120463883623">"vis nummertastatur"</string>
-    <string name="pane_title_emergency_dialpad" msgid="3627372514638694401">"Nummertastatur til nødopkald"</string>
+    <string name="description_dialpad_button" msgid="7395114120463883623">"vis opkaldstastatur"</string>
+    <string name="pane_title_emergency_dialpad" msgid="3627372514638694401">"Nødopkaldstastatur"</string>
     <string name="voicemail_visual_voicemail_switch_title" msgid="6610414098912832120">"Visuel telefonsvarer"</string>
     <string name="voicemail_set_pin_dialog_title" msgid="7005128605986960003">"Angiv pinkode"</string>
     <string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"Skift pinkode"</string>
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index ff4dcfe..01bbe4f 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -2433,7 +2433,11 @@
         return mTelephonySharedPreferences;
     }
 
-    private Phone getDefaultPhone() {
+    /**
+     * Get the default phone for this device.
+     */
+    @VisibleForTesting
+    public Phone getDefaultPhone() {
         Phone thePhone = getPhone(getDefaultSubscription());
         return (thePhone != null) ? thePhone : PhoneFactory.getDefaultPhone();
     }
@@ -11751,6 +11755,10 @@
             throw new UnsupportedOperationException(
                     "Null cipher and integrity operations require HAL 2.1 or above");
         }
+        if (!getDefaultPhone().isNullCipherAndIntegritySupported()) {
+            throw new UnsupportedOperationException(
+                    "Null cipher and integrity operations unsupported by modem");
+        }
     }
 
     /**
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index a5be6c3..0b71feb 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -563,6 +563,7 @@
                             c.removeTelephonyConnectionListener(mNormalCallConnectionListener);
                             mDomainSelectionConnection.finishSelection();
                             mDomainSelectionConnection = null;
+                            mNormalCallConnection = null;
                         }
                     }
                 }
diff --git a/src/com/android/services/telephony/domainselection/ImsStateTracker.java b/src/com/android/services/telephony/domainselection/ImsStateTracker.java
index e1d0d31..95c81a0 100644
--- a/src/com/android/services/telephony/domainselection/ImsStateTracker.java
+++ b/src/com/android/services/telephony/domainselection/ImsStateTracker.java
@@ -773,6 +773,7 @@
             case ImsRegistrationImplBase.REGISTRATION_TECH_NR:
                 return AccessNetworkType.NGRAN;
             case ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN:
+            case ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM:
                 return AccessNetworkType.IWLAN;
             default:
                 return AccessNetworkType.UNKNOWN;
diff --git a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
index 82057d3..146874c 100644
--- a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
@@ -215,6 +215,12 @@
         }
     }
 
+    private boolean isOutOfService() {
+        return (mServiceState.getState() == ServiceState.STATE_OUT_OF_SERVICE
+                || mServiceState.getState() == ServiceState.STATE_POWER_OFF
+                || mServiceState.getState() == ServiceState.STATE_EMERGENCY_ONLY);
+    }
+
     private synchronized void selectDomain() {
         if (mStopDomainSelection || mSelectionAttributes == null
                 || mTransportSelectorCallback == null) {
@@ -225,12 +231,6 @@
         if (mServiceState == null) {
             logd("Waiting for ServiceState callback.");
             return;
-        } else if (mServiceState.getState() == ServiceState.STATE_OUT_OF_SERVICE
-                || mServiceState.getState() == ServiceState.STATE_POWER_OFF
-                || mServiceState.getState() == ServiceState.STATE_EMERGENCY_ONLY) {
-            loge("Cannot place call in current ServiceState: " + mServiceState.getState());
-            notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
-            return;
         }
 
         // Check if this is a re-dial scenario
@@ -240,8 +240,13 @@
             logd("PsDisconnectCause:" + imsReasonInfo.mCode);
             mReselectDomain = false;
             if (imsReasonInfo.mCode == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED) {
-                logd("Redialing over CS");
-                notifyCsSelected();
+                if (isOutOfService()) {
+                    loge("Cannot place call in current ServiceState: " + mServiceState.getState());
+                    notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
+                } else {
+                    logd("Redialing over CS");
+                    notifyCsSelected();
+                }
                 return;
             } else {
                 logd("Redialing cancelled.");
@@ -269,7 +274,12 @@
 
             if (!mImsStateTracker.isImsRegistered()) {
                 logd("IMS is NOT registered");
-                notifyCsSelected();
+                if (isOutOfService()) {
+                    loge("Cannot place call in current ServiceState: " + mServiceState.getState());
+                    notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
+                } else {
+                    notifyCsSelected();
+                }
                 return;
             }
 
@@ -289,11 +299,21 @@
             } else {
                 logd("IMS is not voice capable");
                 // Voice call CS fallback
-                notifyCsSelected();
+                if (isOutOfService()) {
+                    loge("Cannot place call in current ServiceState: " + mServiceState.getState());
+                    notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
+                } else {
+                    notifyCsSelected();
+                }
             }
         } else {
             logd("IMS is not registered or unavailable");
-            notifyCsSelected();
+            if (isOutOfService()) {
+                loge("Cannot place call in current ServiceState: " + mServiceState.getState());
+                notifySelectionTerminated(DisconnectCause.OUT_OF_SERVICE);
+            } else {
+                notifyCsSelected();
+            }
         }
     }
 }
diff --git a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
index 6e4a65f..b6d1087 100644
--- a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
+++ b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
@@ -149,6 +149,7 @@
 
     @Test
     public void setNullCipherAndIntegrityEnabled_successfullyEnable() {
+        whenModemSupportsNullCiphers();
         doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
         doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
         assertFalse(mSharedPreferences.contains(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED));
@@ -161,6 +162,7 @@
 
     @Test
     public void setNullCipherAndIntegrityEnabled_successfullyDisable() {
+        whenModemSupportsNullCiphers();
         doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
         doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
         assertFalse(mSharedPreferences.contains(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED));
@@ -194,10 +196,10 @@
 
     @Test
     public void isNullCipherAndIntegrityPreferenceEnabled() {
+        whenModemSupportsNullCiphers();
         doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
         doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
 
-        assertTrue(mPhoneInterfaceManager.isNullCipherAndIntegrityPreferenceEnabled());
         mPhoneInterfaceManager.setNullCipherAndIntegrityEnabled(false);
         assertFalse(
                 mSharedPreferences.getBoolean(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED, true));
@@ -215,6 +217,18 @@
     }
 
     @Test
+    public void isNullCipherAndIntegrityPreferenceEnabled_lackingModemSupport() {
+        whenModemDoesNotSupportNullCiphers();
+        doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+        doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+
+        assertThrows(UnsupportedOperationException.class, () -> {
+            mPhoneInterfaceManager.isNullCipherAndIntegrityPreferenceEnabled();
+        });
+
+    }
+
+    @Test
     public void isNullCipherAndIntegrityPreferenceEnabled_lackingPermissions() {
         doReturn(201).when(mPhoneInterfaceManager).getHalVersion(anyInt());
         doThrow(SecurityException.class).when(mPhoneInterfaceManager).enforceReadPermission();
@@ -223,4 +237,16 @@
             mPhoneInterfaceManager.isNullCipherAndIntegrityPreferenceEnabled();
         });
     }
+
+    private void whenModemDoesNotSupportNullCiphers() {
+        doReturn(false).when(mPhone).isNullCipherAndIntegritySupported();
+        doReturn(mPhone).when(
+                mPhoneInterfaceManager).getDefaultPhone();
+    }
+
+    private void whenModemSupportsNullCiphers() {
+        doReturn(true).when(mPhone).isNullCipherAndIntegritySupported();
+        doReturn(mPhone).when(
+                mPhoneInterfaceManager).getDefaultPhone();
+    }
 }
diff --git a/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java b/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java
index b00926f..3551593 100644
--- a/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java
@@ -528,6 +528,14 @@
         assertEquals(AccessNetworkType.IWLAN, mImsStateTracker.getImsAccessNetworkType());
 
         callback.onRegistered(new ImsRegistrationAttributes.Builder(
+                ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM).build());
+
+        assertFalse(mImsStateTracker.isImsStateReady());
+        assertTrue(mImsStateTracker.isImsRegistered());
+        assertTrue(mImsStateTracker.isImsRegisteredOverWlan());
+        assertEquals(AccessNetworkType.IWLAN, mImsStateTracker.getImsAccessNetworkType());
+
+        callback.onRegistered(new ImsRegistrationAttributes.Builder(
                 ImsRegistrationImplBase.REGISTRATION_TECH_NONE).build());
 
         assertFalse(mImsStateTracker.isImsStateReady());
@@ -535,7 +543,7 @@
         assertFalse(mImsStateTracker.isImsRegisteredOverWlan());
         assertEquals(AccessNetworkType.UNKNOWN, mImsStateTracker.getImsAccessNetworkType());
 
-        verify(mImsStateListener, times(4)).onImsRegistrationStateChanged();
+        verify(mImsStateListener, times(5)).onImsRegistrationStateChanged();
     }
 
     @Test
diff --git a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
index 832e480..890ca34 100644
--- a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
@@ -77,7 +77,6 @@
     @Mock private Context mMockContext;
     @Mock private ImsManager mMockImsManager;
     @Mock private ImsMmTelManager mMockMmTelManager;
-    @Mock private ServiceState mMockServiceState;
     @Mock private ImsStateTracker mMockImsStateTracker;
     @Mock private DomainSelectorBase.DestroyListener mMockDestroyListener;
 
@@ -153,14 +152,14 @@
         try {
             mNormalCallDomainSelector.selectDomain(null, null);
         } catch (Exception e) {
-            fail("Invalid input params not handled.");
+            fail("Invalid input params not handled." + e.getMessage());
         }
 
         // Case 2: null TransportSelectorCallback
         try {
             mNormalCallDomainSelector.selectDomain(attributes, null);
         } catch (Exception e) {
-            fail("Invalid params (SelectionAttributes) not handled.");
+            fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
         }
 
         // Case 3: null SelectionAttributes
@@ -168,7 +167,7 @@
         try {
             mNormalCallDomainSelector.selectDomain(null, transportSelectorCallback);
         } catch (Exception e) {
-            fail("Invalid params (SelectionAttributes) not handled.");
+            fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
         }
 
         assertTrue(transportSelectorCallback
@@ -185,7 +184,7 @@
         try {
             mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
         } catch (Exception e) {
-            fail("Invalid params (SelectionAttributes) not handled.");
+            fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
         }
 
         assertTrue(transportSelectorCallback
@@ -201,9 +200,9 @@
                         .setExitedFromAirplaneMode(false)
                         .build();
         try {
-            mNormalCallDomainSelector.selectDomain(null, transportSelectorCallback);
+            mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
         } catch (Exception e) {
-            fail("Invalid params (SelectionAttributes) not handled.");
+            fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
         }
 
         assertTrue(transportSelectorCallback
@@ -211,16 +210,16 @@
 
         // Case 6: Emergency Call
         attributes = new DomainSelectionService.SelectionAttributes.Builder(
-                SLOT_ID, SUB_ID_1, SELECTOR_TYPE_UT)
+                SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
                 .setCallId(TEST_CALLID)
                 .setEmergency(true)
                 .setVideoCall(true)
                 .setExitedFromAirplaneMode(false)
                 .build();
         try {
-            mNormalCallDomainSelector.selectDomain(null, transportSelectorCallback);
+            mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
         } catch (Exception e) {
-            fail("Invalid params (SelectionAttributes) not handled.");
+            fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
         }
 
         assertTrue(transportSelectorCallback
@@ -239,10 +238,10 @@
                         .setVideoCall(true)
                         .setExitedFromAirplaneMode(false)
                         .build();
-        mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
         ServiceState serviceState = new ServiceState();
         serviceState.setStateOutOfService();
-        mNormalCallDomainSelector.onServiceStateUpdated(serviceState);
+        initialize(serviceState, false, false, false, false);
+        mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
         assertTrue(transportSelectorCallback
                 .verifyOnSelectionTerminated(DisconnectCause.OUT_OF_SERVICE));
     }
@@ -303,15 +302,23 @@
         assertTrue(transportSelectorCallback.verifyOnWwanSelected());
         assertTrue(transportSelectorCallback
                 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS));
+
+        //Case 5: Backup calling
+        serviceState.setStateOutOfService();
+        initialize(serviceState, true, true, true, true);
+        mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+        assertTrue(transportSelectorCallback.verifyOnWlanSelected());
     }
 
-    class MockTransportSelectorCallback implements TransportSelectorCallback, WwanSelectorCallback {
-        public boolean mCreated = false;
-        public boolean mWlanSelected = false;
-        public boolean mWwanSelected = false;
-        public boolean mSelectionTerminated = false;
-        int mCauseCode = 0;
-        int mSelectedDomain = 0;
+    static class MockTransportSelectorCallback implements TransportSelectorCallback,
+            WwanSelectorCallback {
+        public boolean mCreated;
+        public boolean mWlanSelected;
+        public boolean mWwanSelected;
+        public boolean mSelectionTerminated;
+        public boolean mDomainSelected;
+        int mCauseCode;
+        int mSelectedDomain;
 
         @Override
         public synchronized void onCreated(DomainSelector selector) {
@@ -323,7 +330,7 @@
         public boolean verifyOnCreated() {
             mCreated = false;
             Log.d(TAG, "verifyOnCreated");
-            waitForCallback();
+            waitForCallback(mCreated);
             return mCreated;
         }
 
@@ -336,7 +343,7 @@
 
         public boolean verifyOnWlanSelected() {
             Log.d(TAG, "verifyOnWlanSelected");
-            waitForCallback();
+            waitForCallback(mWlanSelected);
             return mWlanSelected;
         }
 
@@ -356,7 +363,7 @@
         }
 
         public boolean verifyOnWwanSelected() {
-            waitForCallback();
+            waitForCallback(mWwanSelected);
             return mWwanSelected;
         }
 
@@ -370,17 +377,20 @@
 
         public boolean verifyOnSelectionTerminated(int cause) {
             Log.i(TAG, "verifyOnSelectionTerminated - called");
-            if (!mSelectionTerminated) {
-                waitForCallback();
-            }
+            waitForCallback(mSelectionTerminated);
             return (mSelectionTerminated && cause == mCauseCode);
         }
 
-        private synchronized void waitForCallback() {
+        private synchronized void waitForCallback(boolean condition) {
+            long now = System.currentTimeMillis();
+            long deadline = now + 1000;
             try {
-                wait(1000);
+                while (!condition && now < deadline) {
+                    wait(deadline - now);
+                    now = System.currentTimeMillis();
+                }
             } catch (Exception e) {
-                return;
+                Log.i(TAG, e.getMessage());
             }
         }
 
@@ -396,12 +406,14 @@
         public synchronized void onDomainSelected(@NetworkRegistrationInfo.Domain int domain) {
             Log.i(TAG, "onDomainSelected - called");
             mSelectedDomain = domain;
+            mDomainSelected = true;
             notifyAll();
         }
 
         public boolean verifyOnDomainSelected(int domain) {
             Log.i(TAG, "verifyOnDomainSelected - called");
-            waitForCallback();
+            mDomainSelected = false;
+            waitForCallback(mDomainSelected);
             return (domain == mSelectedDomain);
         }
     }