Merge "Move emergency call to main thread" into sc-dev
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 824bb4b..2e82743 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -28,9 +28,6 @@
 import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
 import android.os.ParcelUuid;
 import android.telecom.Conference;
 import android.telecom.Connection;
@@ -87,7 +84,7 @@
 import java.util.Objects;
 import java.util.Queue;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 import java.util.regex.Pattern;
 
 import javax.annotation.Nullable;
@@ -176,8 +173,6 @@
     // destroyed.
     @VisibleForTesting
     public Pair<WeakReference<TelephonyConnection>, Queue<Phone>> mEmergencyRetryCache;
-    private Handler mDdsSwitchHandler;
-    private HandlerThread mHandlerThread;
     private DeviceState mDeviceState = new DeviceState();
 
     /**
@@ -364,27 +359,6 @@
     };
 
     /**
-     * Factory for Handler creation in order to remove flakiness during t esting.
-     */
-    @VisibleForTesting
-    public interface HandlerFactory {
-        HandlerThread createHandlerThread(String name);
-        Handler createHandler(Looper looper);
-    }
-
-    private HandlerFactory mHandlerFactory = new HandlerFactory() {
-        @Override
-        public HandlerThread createHandlerThread(String name) {
-            return new HandlerThread(name);
-        }
-
-        @Override
-        public Handler createHandler(Looper looper) {
-            return new Handler(looper);
-        }
-    };
-
-    /**
      * DisconnectCause depends on PhoneGlobals in order to get a system context. Mock out
      * dependency for testing.
      */
@@ -475,14 +449,6 @@
     }
 
     /**
-     * Override Handler creation factory for testing.
-     */
-    @VisibleForTesting
-    public void setHandlerFactory(HandlerFactory handlerFactory) {
-        mHandlerFactory = handlerFactory;
-    }
-
-    /**
      * Override DisconnectCause creation for testing.
      */
     @VisibleForTesting
@@ -533,16 +499,11 @@
         IntentFilter intentFilter = new IntentFilter(
                 TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
         registerReceiver(mTtyBroadcastReceiver, intentFilter);
-        mHandlerThread = mHandlerFactory.createHandlerThread("DdsSwitchHandlerThread");
-        mHandlerThread.start();
-        Looper looper = mHandlerThread.getLooper();
-        mDdsSwitchHandler = mHandlerFactory.createHandler(looper);
     }
 
     @Override
     public boolean onUnbind(Intent intent) {
         unregisterReceiver(mTtyBroadcastReceiver);
-        mHandlerThread.quitSafely();
         return super.onUnbind(intent);
     }
 
@@ -876,14 +837,9 @@
             } else {
                 final Connection resultConnection = getTelephonyConnection(request, numberToDial,
                         true, handle, phone);
-                mDdsSwitchHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        boolean result = delayDialForDdsSwitch(phone);
-                        Log.i(this,
-                                "onCreateOutgoingConn - delayDialForDdsSwitch result = " + result);
+                delayDialForDdsSwitch(phone, (result) -> {
+                    Log.i(this, "onCreateOutgoingConn - delayDialForDdsSwitch result = " + result);
                         placeOutgoingConnection(request, resultConnection, phone);
-                    }
                 });
                 return resultConnection;
             }
@@ -965,18 +921,14 @@
                 adjustAndPlaceOutgoingConnection(phone, originalConnection, request, numberToDial,
                         handle, originalPhoneType, false);
             } else {
-                mDdsSwitchHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        boolean result = delayDialForDdsSwitch(phone);
-                        Log.i(this, "handleOnComplete - delayDialForDdsSwitch result = " + result);
-                        adjustAndPlaceOutgoingConnection(phone, originalConnection, request,
-                                numberToDial, handle, originalPhoneType, true);
-                        mIsEmergencyCallPending = false;
-                    }
+                delayDialForDdsSwitch(phone, result -> {
+                    Log.i(this, "handleOnComplete - delayDialForDdsSwitch "
+                            + "result = " + result);
+                    adjustAndPlaceOutgoingConnection(phone, originalConnection, request,
+                            numberToDial, handle, originalPhoneType, true);
+                    mIsEmergencyCallPending = false;
                 });
             }
-
         } else {
             Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
             closeOrDestroyConnection(originalConnection,
@@ -1978,18 +1930,32 @@
     /**
      * If needed, block until the the default data is is switched for outgoing emergency call, or
      * timeout expires.
+     * @param phone The Phone to switch the DDS on.
+     * @param completeConsumer The consumer to call once the default data subscription has been
+     *                         switched, provides {@code true} result if the switch happened
+     *                         successfully or {@code false} if the operation timed out/failed.
      */
-    private boolean delayDialForDdsSwitch(Phone phone) {
+    private void delayDialForDdsSwitch(Phone phone, Consumer<Boolean> completeConsumer) {
         if (phone == null) {
-            return true;
+            // Do not block indefinitely.
+            completeConsumer.accept(false);
         }
         try {
-            return possiblyOverrideDefaultDataForEmergencyCall(phone).get(
-                    DEFAULT_DATA_SWITCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            // Waiting for PhoneSwitcher to complete the operation.
+            CompletableFuture<Boolean> future = possiblyOverrideDefaultDataForEmergencyCall(phone);
+            // In the case that there is an issue or bug in PhoneSwitcher logic, do not wait
+            // indefinitely for the future to complete. Instead, set a timeout that will complete
+            // the future as to not block the outgoing call indefinitely.
+            CompletableFuture<Boolean> timeout = new CompletableFuture<>();
+            phone.getContext().getMainThreadHandler().postDelayed(
+                    () -> timeout.complete(false), DEFAULT_DATA_SWITCH_TIMEOUT_MS);
+            // Also ensure that the Consumer is completed on the main thread.
+            future.acceptEitherAsync(timeout, completeConsumer,
+                    phone.getContext().getMainExecutor());
         } catch (Exception e) {
             Log.w(this, "delayDialForDdsSwitch - exception= "
                     + e.getMessage());
-            return false;
+
         }
     }
 
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 84ab543..898b5ad 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -26,7 +26,6 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -43,8 +42,6 @@
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
 import android.telecom.ConnectionRequest;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
@@ -128,7 +125,6 @@
     @Mock TelephonyConnectionService.PhoneSwitcherProxy mPhoneSwitcherProxy;
     @Mock TelephonyConnectionService.PhoneNumberUtilsProxy mPhoneNumberUtilsProxy;
     @Mock TelephonyConnectionService.PhoneUtilsProxy mPhoneUtilsProxy;
-    @Mock TelephonyConnectionService.HandlerFactory mHandlerFactory;
     @Mock TelephonyConnectionService.DisconnectCauseFactory mDisconnectCauseFactory;
     @Mock Handler mMockHandler;
     @Mock EmergencyNumberTracker mEmergencyNumberTracker;
@@ -175,11 +171,6 @@
                 .thenAnswer(invocation -> invocation.getArgument(1));
         mTestConnectionService.setPhoneNumberUtilsProxy(mPhoneNumberUtilsProxy);
         mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
-        HandlerThread mockHandlerThread = mock(HandlerThread.class);
-        doReturn(mockHandlerThread).when(mHandlerFactory).createHandlerThread(anyString());
-        doReturn(null).when(mockHandlerThread).getLooper();
-        doReturn(mMockHandler).when(mHandlerFactory).createHandler(any());
-        mTestConnectionService.setHandlerFactory(mHandlerFactory);
         mTestConnectionService.setDeviceState(mDeviceState);
         mTestConnectionService.setRadioOnHelper(mRadioOnHelper);
         doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
@@ -960,22 +951,20 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial_carrierconfig_dds() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
         // Setup test to not support SUPL on the non-DDS subscription
         doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
                 null);
-        testPhone.getServiceState().setRoaming(false);
         getTestContext().getCarrierConfig(0 /*subId*/).putInt(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "150");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                false /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
+                        null /* operator short name */, null /* operator numeric name */);
         verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
                 eq(150) /*extensionTime*/, any());
     }
@@ -1026,11 +1015,9 @@
         assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
 
         callback.getValue().onComplete(null, true);
-        Runnable delayDialRunnable = verifyRunnablePosted();
 
         try {
             doAnswer(invocation -> null).when(mContext).startActivity(any());
-            delayDialRunnable.run();
             verify(testPhone).dial(anyString(), any());
         } catch (CallStateException e) {
             // This shouldn't happen
@@ -1045,22 +1032,20 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial_nocarrierconfig() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
         // Setup test to not support SUPL on the non-DDS subscription
         doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
                 null);
-        testPhone.getServiceState().setRoaming(false);
         getTestContext().getCarrierConfig(0 /*subId*/).putInt(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                false /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
+                        null /* operator short name */, null /* operator numeric name */);
         verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
     }
 
@@ -1071,22 +1056,20 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial_supportsuplondds() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
         // If the non-DDS supports SUPL, dont switch data
         doReturn(false).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
                 null);
-        testPhone.getServiceState().setRoaming(false);
         getTestContext().getCarrierConfig(0 /*subId*/).putInt(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                false /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
+                         null /* operator short name */, null /* operator numeric name */);
         verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
     }
 
@@ -1097,22 +1080,20 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial_roaming_nocarrierconfig() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
         // Setup test to not support SUPL on the non-DDS subscription
         doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
                 null);
-        testPhone.getServiceState().setRoaming(true);
         getTestContext().getCarrierConfig(0 /*subId*/).putInt(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                true /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
+                         null /* operator short name */, null /* operator numeric name */);
         verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
     }
 
@@ -1124,16 +1105,10 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial_roamingcarrierconfig() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         // Setup voice roaming scenario
         String testRoamingOperator = "001001";
-        // In some roaming conditions, we are not technically "roaming"
-        testPhone.getServiceState().setRoaming(false);
-        testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
         // Setup test to not support SUPL on the non-DDS subscription
-        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         String[] roamingPlmns = new String[1];
         roamingPlmns[0] = testRoamingOperator;
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
@@ -1144,8 +1119,11 @@
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                false /* isRoaming */, true /* setOperatorName */,
+                        "TestTel" /* operator long name*/, "TestTel" /* operator short name */,
+                                testRoamingOperator /* operator numeric name */);
         verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
                 eq(0) /*extensionTime*/, any());
     }
@@ -1158,15 +1136,10 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial__roaming_roamingcarrierconfig() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
-        // Setup voice roaming scenario
-        String testRoamingOperator = "001001";
-        testPhone.getServiceState().setRoaming(true);
-        testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
         // Setup test to not support SUPL on the non-DDS subscription
         doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        // Setup voice roaming scenario
+        String testRoamingOperator = "001001";
         String[] roamingPlmns = new String[1];
         roamingPlmns[0] = testRoamingOperator;
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
@@ -1177,8 +1150,11 @@
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                false /* isRoaming */, true /* setOperatorName */,
+                        "TestTel" /* operator long name*/, "TestTel" /* operator short name */,
+                                testRoamingOperator /* operator numeric name */);
         verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
                 eq(0) /*extensionTime*/, any());
     }
@@ -1383,9 +1359,16 @@
 
     /**
      * Set up a mock MSIM device with TEST_ADDRESS set as an emergency number.
+     * @param isRoaming whether it is roaming
+     * @param setOperatorName whether operator name needs to set
+     * @param operatorNameLongName the operator long name if needs to set
+     * @param operatorNameShortName the operator short name if needs to set
+     * @param operatorNameNumeric the operator numeric name if needs to set
      * @return the Phone associated with slot 0.
      */
-    private Phone setupConnectionServiceForDelayDial() {
+    private Phone setupConnectionServiceForDelayDial(boolean isRoaming, boolean setOperatorName,
+            String operatorNameLongName, String operatorNameShortName,
+                    String operatorNameNumeric) {
         ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
                 .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
                 .setAddress(TEST_ADDRESS)
@@ -1410,15 +1393,17 @@
         emergencyNumbers.put(0 /*subId*/, numbers);
         doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
         doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
-
+        testPhone0.getServiceState().setRoaming(isRoaming);
+        if (setOperatorName) {
+            testPhone0.getServiceState().setOperatorName(operatorNameLongName,
+                    operatorNameShortName, operatorNameNumeric);
+        }
         mConnection = mTestConnectionService.onCreateOutgoingConnection(
                 PHONE_ACCOUNT_HANDLE_1, connectionRequest);
         assertNotNull("test connection was not set up correctly.", mConnection);
-
         return testPhone0;
     }
 
-
     /**
      * Set up a mock MSIM device with TEST_ADDRESS set as an emergency number in airplane mode.
      * @return the Phone associated with slot 0.
@@ -1458,15 +1443,6 @@
         return testPhone0;
     }
 
-    private Runnable verifyRunnablePosted() {
-        ArgumentCaptor<Message> runnableCaptor = ArgumentCaptor.forClass(Message.class);
-        verify(mMockHandler).sendMessageDelayed(runnableCaptor.capture(), anyLong());
-        assertNotNull("Invalid Message created", runnableCaptor.getValue());
-        Runnable runnable = runnableCaptor.getValue().getCallback();
-        assertNotNull("sendMessageDelayed never occurred.", runnableCaptor);
-        return runnable;
-    }
-
     private EmergencyNumber setupEmergencyNumber(Uri address) {
         return new EmergencyNumber(address.getSchemeSpecificPart(), "", "",
         EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,