Merge "Remove Thread.sleep in Telephony unittests"
diff --git a/tests/telephonytests/Android.bp b/tests/telephonytests/Android.bp
index 0978083..772a9c9 100644
--- a/tests/telephonytests/Android.bp
+++ b/tests/telephonytests/Android.bp
@@ -20,6 +20,7 @@
         "services.net",
         "telephony-common",
         "truth-prebuilt",
+        "testables",
     ],
 
     platform_apis: true,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index 655d8f9..bf36e73 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -29,17 +29,19 @@
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.Intent;
-import android.os.HandlerThread;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
 
 import java.util.Map;
@@ -47,32 +49,19 @@
 /**
  * Unit tests for {@link com.android.internal.telephony.CarrierServiceStateTracker}.
  */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class CarrierServiceStateTrackerTest extends TelephonyTest {
     public static final String LOG_TAG = "CSST";
-    public static final int TEST_TIMEOUT = 5000;
 
     private CarrierServiceStateTracker mSpyCarrierSST;
     private CarrierServiceStateTracker mCarrierSST;
-    private CarrierServiceStateTrackerTestHandler mCarrierServiceStateTrackerTestHandler;
 
     private static final int SUB_ID = 1;
 
     NotificationManager mNotificationManager;
     PersistableBundle mBundle;
 
-    private class CarrierServiceStateTrackerTestHandler extends HandlerThread {
-        private CarrierServiceStateTrackerTestHandler(String name) {
-            super(name);
-        }
-
-        @Override
-        public void onLooperPrepared() {
-            mCarrierSST = new CarrierServiceStateTracker(mPhone, mSST);
-            mSpyCarrierSST = spy(mCarrierSST);
-            setReady(true);
-        }
-    }
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -80,27 +69,23 @@
         super.setUp(getClass().getSimpleName());
         mBundle = mContextFixture.getCarrierConfigBundle();
         when(mPhone.getSubId()).thenReturn(SUB_ID);
-        mCarrierServiceStateTrackerTestHandler =
-                new CarrierServiceStateTrackerTestHandler(getClass().getSimpleName());
-        mCarrierServiceStateTrackerTestHandler.start();
+        mCarrierSST = new CarrierServiceStateTracker(mPhone, mSST);
+        mSpyCarrierSST = spy(mCarrierSST);
 
         mNotificationManager = (NotificationManager) mContext.getSystemService(
                 Context.NOTIFICATION_SERVICE);
 
         setDefaultValues();
-        waitUntilReady();
+        processAllMessages();
     }
 
     private void setDefaultValues() {
-        mBundle.putInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT,
-                0);
-        mBundle.putInt(CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT,
-                0);
+        mBundle.putInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, 0);
+        mBundle.putInt(CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT, 0);
     }
 
     @After
     public void tearDown() throws Exception {
-        mCarrierServiceStateTrackerTestHandler.quit();
         super.tearDown();
     }
 
@@ -113,7 +98,7 @@
         doReturn(false).when(mSpyCarrierSST).evaluateSendingMessage(any());
         doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
         mSpyCarrierSST.handleMessage(notificationMsg);
-        waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
+        processAllMessages();
         verify(mNotificationManager).cancel(
                 CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG, SUB_ID);
         verify(mNotificationManager).cancel(
@@ -134,7 +119,7 @@
         doReturn(mNotificationBuilder).when(mSpyCarrierSST).getNotificationBuilder(any());
         doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
         mSpyCarrierSST.handleMessage(notificationMsg);
-        waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
+        processAllMessages();
         verify(mNotificationManager).notify(
                 eq(CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG),
                 eq(SUB_ID), isA(Notification.class));
@@ -149,7 +134,7 @@
         logd(LOG_TAG + ":testSendPrefNetworkNotification()");
         Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         mContext.sendBroadcast(intent);
-        waitForLastHandlerAction(mCarrierServiceStateTrackerTestHandler.getThreadHandler());
+        processAllMessages();
 
         Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
                 mCarrierSST.getNotificationTypeMap();
@@ -170,7 +155,7 @@
                 RILConstants.NETWORK_MODE_LTE_CDMA_EVDO);
         mSpyCarrierSST.getContentObserver().dispatchChange(false,
                 Settings.Global.getUriFor(prefNetworkMode));
-        waitForLastHandlerAction(mCarrierServiceStateTrackerTestHandler.getThreadHandler());
+        processAllMessages();
         verify(mNotificationManager, atLeast(1)).notify(
                 eq(CarrierServiceStateTracker.PREF_NETWORK_NOTIFICATION_TAG),
                 eq(SUB_ID), isA(Notification.class));
@@ -180,7 +165,7 @@
                 RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
         mSpyCarrierSST.getContentObserver().dispatchChange(false,
                 Settings.Global.getUriFor(prefNetworkMode));
-        waitForLastHandlerAction(mCarrierServiceStateTrackerTestHandler.getThreadHandler());
+        processAllMessages();
         verify(mNotificationManager, atLeast(1)).cancel(
                 CarrierServiceStateTracker.PREF_NETWORK_NOTIFICATION_TAG, SUB_ID);
     }
@@ -191,7 +176,7 @@
         logd(LOG_TAG + ":testSendEmergencyNetworkNotification()");
         Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         mContext.sendBroadcast(intent);
-        waitForLastHandlerAction(mCarrierServiceStateTrackerTestHandler.getThreadHandler());
+        processAllMessages();
 
         Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
                 mCarrierSST.getNotificationTypeMap();
@@ -210,14 +195,14 @@
         Message notificationMsg = mSpyCarrierSST.obtainMessage(
                 CarrierServiceStateTracker.CARRIER_EVENT_IMS_CAPABILITIES_CHANGED, null);
         mSpyCarrierSST.handleMessage(notificationMsg);
-        waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
+        processAllMessages();
         verify(mNotificationManager).notify(
                 eq(CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG),
                 eq(SUB_ID), isA(Notification.class));
 
         doReturn(false).when(mPhone).isWifiCallingEnabled();
         mSpyCarrierSST.handleMessage(notificationMsg);
-        waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
+        processAllMessages();
         verify(mNotificationManager, atLeast(2)).cancel(
                 CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG, SUB_ID);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
index 97b8460..8e5035c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
@@ -20,6 +20,7 @@
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
 
+import static com.android.internal.telephony.PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS;
 import static com.android.internal.telephony.PhoneSwitcher.EVENT_DATA_ENABLED_CHANGED;
 import static com.android.internal.telephony.PhoneSwitcher.EVENT_PRECISE_CALL_STATE_CHANGED;
 
@@ -36,7 +37,6 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -48,14 +48,14 @@
 import android.net.StringNetworkSpecifier;
 import android.os.AsyncResult;
 import android.os.Handler;
-import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.telephony.PhoneCapability;
 import android.telephony.SubscriptionManager;
 import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.test.runner.AndroidJUnit4;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 
 import org.junit.After;
 import org.junit.Before;
@@ -65,11 +65,10 @@
 import org.mockito.Mock;
 
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
 
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class PhoneSwitcherTest extends TelephonyTest {
     private static final String[] sNetworkAttributes = new String[] {
             "mobile,0,0,0,-1,true", "mobile_mms,2,0,2,60000,true",
@@ -102,9 +101,9 @@
     private ISetOpportunisticDataCallback mSetOpptDataCallback1;
     @Mock
     private ISetOpportunisticDataCallback mSetOpptDataCallback2;
+    @Mock
+    CompletableFuture<Boolean> mFuturePhone;
 
-    // The thread that mPhoneSwitcher will handle events in.
-    private HandlerThread mHandlerThread;
     private PhoneSwitcher mPhoneSwitcher;
     private IOnSubscriptionsChangedListener mSubChangedListener;
     private ConnectivityManager mConnectivityManager;
@@ -149,7 +148,6 @@
         assertFalse("data allowed initially", mDataAllowed[1]);
 
         NetworkRequest internetNetworkRequest = addInternetNetworkRequest(null, 50);
-        waitABit();
 
         assertFalse("phone active after request", mPhoneSwitcher
                 .shouldApplyNetworkRequest(internetNetworkRequest, 0));
@@ -164,13 +162,12 @@
         clearInvocations(mActivePhoneSwitchHandler);
 
         setDefaultDataSubId(0);
-        waitABit();
 
         verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
 
         setSlotIndexToSubId(0, 0);
         mSubChangedListener.onSubscriptionsChanged();
-        waitABit();
+        processAllMessages();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -190,7 +187,6 @@
 
         // 1 lose default via default sub change
         setDefaultDataSubId(1);
-        waitABit();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -198,7 +194,7 @@
 
         setSlotIndexToSubId(1, 1);
         mSubChangedListener.onSubscriptionsChanged();
-        waitABit();
+        processAllMessages();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -207,7 +203,6 @@
 
         // 2 gain default via default sub change
         setDefaultDataSubId(0);
-        waitABit();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -217,7 +212,7 @@
         // 3 lose default via sub->phone change
         setSlotIndexToSubId(0, 2);
         mSubChangedListener.onSubscriptionsChanged();
-        waitABit();
+        processAllMessages();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -227,7 +222,7 @@
         // 4 gain default via sub->phone change
         setSlotIndexToSubId(0, 0);
         mSubChangedListener.onSubscriptionsChanged();
-        waitABit();
+        processAllMessages();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -236,7 +231,6 @@
 
         // 5 lose default network request
         releaseNetworkRequest(internetNetworkRequest);
-        waitABit();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -245,7 +239,6 @@
 
         // 6 gain subscription-specific request
         NetworkRequest specificInternetRequest = addInternetNetworkRequest(0, 50);
-        waitABit();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -255,7 +248,7 @@
         // 7 lose via sub->phone change
         setSlotIndexToSubId(0, 1);
         mSubChangedListener.onSubscriptionsChanged();
-        waitABit();
+        processAllMessages();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -265,7 +258,7 @@
         // 8 gain via sub->phone change
         setSlotIndexToSubId(0, 0);
         mSubChangedListener.onSubscriptionsChanged();
-        waitABit();
+        processAllMessages();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -274,7 +267,6 @@
 
         // 9 lose subscription-specific request
         releaseNetworkRequest(specificInternetRequest);
-        waitABit();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -285,7 +277,7 @@
         // not ready yet - Phone turns out to be hard to stub out
 //        phones[0].setInEmergencyCall(true);
 //        connectivityServiceMock.addDefaultRequest();
-//        waitABit();
+//        processAllMessages();
 //        if (testHandler.getActivePhoneSwitchCount() != 11) {
 //            fail("after release of request, ActivePhoneSwitchCount not 11!");
 //        }
@@ -294,14 +286,12 @@
 //
 //        phones[0].setInEmergencyCall(false);
 //        connectivityServiceMock.addDefaultRequest();
-//        waitABit();
+//        processAllMessages();
 //        if (testHandler.getActivePhoneSwitchCount() != 12) {
 //            fail("after release of request, ActivePhoneSwitchCount not 11!");
 //        }
 //        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
 //        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
-
-        mHandlerThread.quit();
     }
 
     /**
@@ -331,10 +321,9 @@
         setSlotIndexToSubId(0, 0);
         setSlotIndexToSubId(1, 1);
         setDefaultDataSubId(0);
-        waitABit();
         mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
                 ACTIVE_PHONE_SWITCH, null);
-        waitABit();
+        processAllMessages();
         // verify initial conditions
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
 
@@ -343,14 +332,11 @@
 
         // now start a higher priority conneciton on the other sub
         addMmsNetworkRequest(1);
-        waitABit();
 
         // After gain of network request, mActivePhoneSwitchHandler should be notified 2 times.
         verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
         assertFalse("data allowed", mDataAllowed[0]);
         assertTrue("data not allowed", mDataAllowed[1]);
-
-        mHandlerThread.quit();
     }
 
     /**
@@ -365,32 +351,26 @@
         initialize(numPhones, maxActivePhones);
 
         addInternetNetworkRequest(null, 50);
-        waitABit();
 
         setSlotIndexToSubId(0, 0);
         setSlotIndexToSubId(1, 1);
         setDefaultDataSubId(0);
-        waitABit();
 
         // Phone 0 should be active
         assertTrue("data not allowed", mDataAllowed[0]);
         assertFalse("data allowed", mDataAllowed[1]);
 
         addInternetNetworkRequest(null, 100);
-        waitABit();
 
         // should be no change
         assertTrue("data not allowed", mDataAllowed[0]);
         assertFalse("data allowed", mDataAllowed[1]);
 
         addInternetNetworkRequest(null, 0);
-        waitABit();
 
         // should be no change
         assertTrue("data not allowed", mDataAllowed[0]);
         assertFalse("data allowed", mDataAllowed[1]);
-
-        mHandlerThread.quit();
     }
 
     /**
@@ -415,24 +395,21 @@
 
         // Notify phoneSwitcher about default data sub and default network request.
         addInternetNetworkRequest(null, 50);
-        waitABit();
         // Phone 0 (sub 1) should be activated as it has default data sub.
         assertTrue(mDataAllowed[0]);
 
         // Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
         mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, null);
-        waitABit();
+        processAllMessages();
         assertFalse(mDataAllowed[0]);
         assertTrue(mDataAllowed[1]);
 
         // Unset preferred sub should make default data sub (phone 0 / sub 1) activated again.
         mPhoneSwitcher.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
-        waitABit();
+        processAllMessages();
         assertTrue(mDataAllowed[0]);
         assertFalse(mDataAllowed[1]);
-
-        mHandlerThread.quit();
     }
 
     @Test
@@ -456,7 +433,6 @@
         setSlotIndexToSubId(0, 1);
         setSlotIndexToSubId(1, 2);
         setDefaultDataSubId(1);
-        waitABit();
         // Phone 0 (sub 1) should be preferred data phone as it has default data sub.
         verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
@@ -468,7 +444,6 @@
         // It shouldn't change anything.
         NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
         NetworkRequest mmsRequest = addMmsNetworkRequest(2);
-        waitABit();
         verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
         verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
         assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
@@ -478,7 +453,7 @@
 
         // Set sub 2 as preferred sub should make phone 1 preferredDataModem
         mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, null);
-        waitABit();
+        processAllMessages();
         verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
         verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
         assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
@@ -492,7 +467,7 @@
         // Unset preferred sub should make phone0 preferredDataModem again.
         mPhoneSwitcher.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
-        waitABit();
+        processAllMessages();
         verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
         assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
@@ -503,9 +478,6 @@
         // SetDataAllowed should never be triggered.
         verify(mCommandsInterface0, never()).setDataAllowed(anyBoolean(), any());
         verify(mCommandsInterface1, never()).setDataAllowed(anyBoolean(), any());
-
-        mHandlerThread.quit();
-
     }
 
     @Test
@@ -524,39 +496,36 @@
         setSlotIndexToSubId(0, 1);
         setSlotIndexToSubId(1, 2);
         setDefaultDataSubId(1);
-        waitABit();
 
         // Phone 0 (sub 1) should be activated as it has default data sub.
         assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
 
         // Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
         mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, null);
-        waitABit();
+        processAllMessages();
         verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false),
                 eq(mPhoneSwitcher.mValidationCallback));
         // Validation failed. Preferred data sub should remain 1, data phone should remain 0.
         mPhoneSwitcher.mValidationCallback.onValidationResult(false, 2);
-        waitABit();
+        processAllMessages();
         assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
 
         // Validation succeeds. Preferred data sub changes to 2, data phone changes to 1.
         mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, null);
-        waitABit();
+        processAllMessages();
         mPhoneSwitcher.mValidationCallback.onValidationResult(true, 2);
-        waitABit();
+        processAllMessages();
         assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
 
         // Switching data back to primary (subId 1).
         mPhoneSwitcher.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, null);
-        waitABit();
+        processAllMessages();
         verify(mCellularNetworkValidator).validate(eq(1), anyInt(), eq(false),
                 eq(mPhoneSwitcher.mValidationCallback));
         mPhoneSwitcher.mValidationCallback.onValidationResult(true, 1);
-        waitABit();
+        processAllMessages();
         assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
-
-        mHandlerThread.quit();
     }
 
     @Test
@@ -572,9 +541,7 @@
         setSlotIndexToSubId(0, 1);
         setSlotIndexToSubId(1, 2);
         setDefaultDataSubId(1);
-        waitABit();
         NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
-        waitABit();
         assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
         assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
         clearInvocations(mMockRadioConfig);
@@ -607,8 +574,6 @@
         verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
         assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
         assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
-
-        mHandlerThread.quit();
     }
 
 
@@ -625,19 +590,14 @@
         setSlotIndexToSubId(0, 1);
         setSlotIndexToSubId(1, 2);
         setDefaultDataSubId(1);
-        waitABit();
         NetworkRequest internetRequest = addInternetNetworkRequest(2, 50);
-        waitABit();
         assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
         assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
 
         // Restricted network request will should be applied.
         internetRequest = addInternetNetworkRequest(2, 50, true);
-        waitABit();
         assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
         assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
-
-        mHandlerThread.quit();
     }
 
     @Test
@@ -654,19 +614,12 @@
         clearInvocations(mMockRadioConfig);
 
         // override the phone ID in prep for emergency call
-        CountDownLatch latch = new CountDownLatch(1);
-        CompletableFuture<Boolean> futurePhone = new CompletableFuture<>();
-        futurePhone.whenComplete((result, error) -> {
-            assertTrue(result);
-            latch.countDown();
-        });
-        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, futurePhone);
+        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
         sendPreferredDataSuccessResult(1);
-        assertTrue(latch.await(2, TimeUnit.SECONDS));
+        processAllMessages();
+        verify(mFuturePhone).complete(true);
         // Make sure the correct broadcast is sent out for the overridden phone ID
         verify(mTelRegistryInterfaceMock).notifyActiveDataSubIdChanged(eq(2));
-
-        mHandlerThread.quit();
     }
 
     @Test
@@ -683,19 +636,12 @@
         clearInvocations(mMockRadioConfig);
 
         // override the phone ID in prep for emergency call
-        CountDownLatch latch = new CountDownLatch(1);
-        CompletableFuture<Boolean> futurePhone = new CompletableFuture<>();
-        futurePhone.whenComplete((result, error) -> {
-            assertTrue(result);
-            latch.countDown();
-        });
-        mPhoneSwitcher.overrideDefaultDataForEmergency(0, 1, futurePhone);
-        waitABit();
+        mPhoneSwitcher.overrideDefaultDataForEmergency(0, 1, mFuturePhone);
+        processAllMessages();
         // The radio command should never be called because the DDS hasn't changed.
         verify(mMockRadioConfig, never()).setPreferredDataModem(eq(0), any());
-        assertTrue(latch.await(2, TimeUnit.SECONDS));
-
-        mHandlerThread.quit();
+        processAllMessages();
+        verify(mFuturePhone).complete(true);
     }
 
     @Test
@@ -715,28 +661,22 @@
         clearInvocations(mTelRegistryInterfaceMock);
 
         // override the phone ID in prep for emergency call
-        CountDownLatch latch = new CountDownLatch(1);
-        CompletableFuture<Boolean> futurePhone = new CompletableFuture<>();
-        futurePhone.whenComplete((result, error) -> {
-            assertTrue(result);
-            latch.countDown();
-        });
-        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, futurePhone);
+        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
         sendPreferredDataSuccessResult(1);
-        assertTrue(latch.await(1, TimeUnit.SECONDS));
+        processAllMessages();
+        verify(mFuturePhone).complete(true);
 
         // Start and end the emergency call, which will start override timer
         notifyPhoneAsInCall(mPhone2);
         notifyPhoneAsInactive(mPhone2);
 
+        clearInvocations(mTelRegistryInterfaceMock);
         // Verify that the DDS is successfully switched back after 1 second + base ECBM timeout
-        verify(mMockRadioConfig,
-                timeout(PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 2000))
-                .setPreferredDataModem(eq(0), any());
+        moveTimeForward(ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 1000);
+        processAllMessages();
+        verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         // Make sure the correct broadcast is sent out for the phone ID
         verify(mTelRegistryInterfaceMock).notifyActiveDataSubIdChanged(eq(1));
-
-        mHandlerThread.quit();
     }
 
     @Test
@@ -756,15 +696,10 @@
         clearInvocations(mTelRegistryInterfaceMock);
 
         // override the phone ID in prep for emergency call
-        CountDownLatch latch = new CountDownLatch(1);
-        CompletableFuture<Boolean> futurePhone = new CompletableFuture<>();
-        futurePhone.whenComplete((result, error) -> {
-            assertTrue(result);
-            latch.countDown();
-        });
-        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, futurePhone);
+        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
         sendPreferredDataSuccessResult(1);
-        assertTrue(latch.await(1, TimeUnit.SECONDS));
+        processAllMessages();
+        verify(mFuturePhone).complete(true);
 
         // Start and end the emergency call, which will start override timer
         notifyPhoneAsInCall(mPhone2);
@@ -775,21 +710,22 @@
 
         // DDS should not be switched back until ECBM ends, make sure there is no further
         // interaction.
-        Thread.sleep(PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 2000);
+        moveTimeForward(ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 2000);
+        processAllMessages();
         verify(mMockRadioConfig, never()).setPreferredDataModem(eq(0), any());
         // Make sure the correct broadcast is sent out for the phone ID
         verify(mTelRegistryInterfaceMock).notifyActiveDataSubIdChanged(eq(2));
 
         // End ECBM
+        clearInvocations(mTelRegistryInterfaceMock);
         ecbmMessage = getEcbmRegistration(mPhone2);
         notifyEcbmEnd(mPhone2, ecbmMessage);
         // Verify that the DDS is successfully switched back after 1 second.
-        verify(mMockRadioConfig, timeout(2000)).setPreferredDataModem(eq(0), any());
+        moveTimeForward(1000);
+        processAllMessages();
+        verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         // Make sure the correct broadcast is sent out for the phone ID
         verify(mTelRegistryInterfaceMock).notifyActiveDataSubIdChanged(eq(1));
-
-
-        mHandlerThread.quit();
     }
 
     @Test
@@ -809,25 +745,17 @@
         clearInvocations(mTelRegistryInterfaceMock);
 
         // override the phone ID in prep for emergency call
-        CountDownLatch latch = new CountDownLatch(1);
-        CompletableFuture<Boolean> futurePhone = new CompletableFuture<>();
-        futurePhone.whenComplete((result, error) -> {
-            assertTrue(result);
-            latch.countDown();
-        });
-        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, futurePhone);
+        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
         sendPreferredDataSuccessResult(1);
-        assertTrue(latch.await(1, TimeUnit.SECONDS));
+        processAllMessages();
+        verify(mFuturePhone).complete(true);
 
         // Do not start the call and make sure the override is removed once the timeout expires
-        verify(mMockRadioConfig,
-                timeout(PhoneSwitcher.DEFAULT_DATA_OVERRIDE_TIMEOUT_MS + 1000))
-                .setPreferredDataModem(eq(0), any());
+        moveTimeForward(PhoneSwitcher.DEFAULT_DATA_OVERRIDE_TIMEOUT_MS);
+        processAllMessages();
+        verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         // Make sure the correct broadcast is sent out for the phone ID
         verify(mTelRegistryInterfaceMock).notifyActiveDataSubIdChanged(eq(1));
-
-
-        mHandlerThread.quit();
     }
 
     @Test
@@ -852,7 +780,8 @@
         futurePhone.whenComplete((r, error) -> queue.offer(r));
         mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, futurePhone);
         sendPreferredDataSuccessResult(1);
-        Boolean result = queue.poll(1, TimeUnit.SECONDS);
+        processAllMessages();
+        Boolean result = queue.poll();
         assertNotNull(result);
         assertTrue(result);
 
@@ -860,7 +789,8 @@
         futurePhone = new CompletableFuture<>();
         futurePhone.whenComplete((r, error) -> queue.offer(r));
         mPhoneSwitcher.overrideDefaultDataForEmergency(0, 1, futurePhone);
-        result = queue.poll(1, TimeUnit.SECONDS);
+        processAllMessages();
+        result = queue.poll();
         assertNotNull(result);
         assertFalse(result);
         verify(mMockRadioConfig, never()).setPreferredDataModem(eq(0), any());
@@ -870,14 +800,11 @@
         notifyPhoneAsInactive(mPhone2);
 
         // Verify that the DDS is successfully switched back after 1 second + base ECBM timeout
-        verify(mMockRadioConfig,
-                timeout(PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 2000))
-                .setPreferredDataModem(eq(0), any());
+        moveTimeForward(ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 1000);
+        processAllMessages();
+        verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         // Make sure the correct broadcast is sent out for the phone ID
         verify(mTelRegistryInterfaceMock).notifyActiveDataSubIdChanged(eq(1));
-
-
-        mHandlerThread.quit();
     }
 
     @Test
@@ -896,89 +823,87 @@
         setSlotIndexToSubId(0, 1);
         setSlotIndexToSubId(1, 2);
         setDefaultDataSubId(1);
-        waitABit();
 
         // Validating on sub 10 which is inactive.
         mPhoneSwitcher.trySetOpportunisticDataSubscription(10, true, mSetOpptDataCallback1);
-        waitABit();
+        processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
 
         // Switch to active subId without validating. Should always succeed.
         mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
-        waitABit();
+        processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
 
         // Validating on sub 1 and fails.
         clearInvocations(mSetOpptDataCallback1);
         mPhoneSwitcher.trySetOpportunisticDataSubscription(1, true, mSetOpptDataCallback1);
-        waitABit();
+        processAllMessages();
         mPhoneSwitcher.mValidationCallback.onValidationResult(false, 1);
-        waitABit();
+        processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
 
         // Validating on sub 2 and succeeds.
         mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
-        waitABit();
+        processAllMessages();
         mPhoneSwitcher.mValidationCallback.onValidationResult(true, 2);
-        waitABit();
+        processAllMessages();
         verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
 
         // Switching data back to primary and validation fails.
         clearInvocations(mSetOpptDataCallback2);
         mPhoneSwitcher.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
-        waitABit();
+        processAllMessages();
         mPhoneSwitcher.mValidationCallback.onValidationResult(false, 1);
-        waitABit();
+        processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
 
         // Switching data back to primary and succeeds.
         clearInvocations(mSetOpptDataCallback2);
         mPhoneSwitcher.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
-        waitABit();
+        processAllMessages();
         mPhoneSwitcher.mValidationCallback.onValidationResult(true, 1);
-        waitABit();
+        processAllMessages();
         verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
 
         // Back to back call on same subId.
         clearInvocations(mSetOpptDataCallback1);
         clearInvocations(mSetOpptDataCallback2);
         mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
-        waitABit();
+        processAllMessages();
         verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false),
                 eq(mPhoneSwitcher.mValidationCallback));
         doReturn(true).when(mCellularNetworkValidator).isValidating();
         mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
-        waitABit();
+        processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
         verify(mSetOpptDataCallback2, never()).onComplete(anyInt());
         // Validation succeeds.
         doReturn(false).when(mCellularNetworkValidator).isValidating();
         mPhoneSwitcher.mValidationCallback.onValidationResult(true, 2);
-        waitABit();
+        processAllMessages();
         verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
 
         mPhoneSwitcher.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
-        waitABit();
+        processAllMessages();
         clearInvocations(mSetOpptDataCallback1);
         clearInvocations(mSetOpptDataCallback2);
         clearInvocations(mCellularNetworkValidator);
         // Back to back call, call 1 to switch to subId 2, call 2 to switch back.
         mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
-        waitABit();
+        processAllMessages();
         verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false),
                 eq(mPhoneSwitcher.mValidationCallback));
         doReturn(true).when(mCellularNetworkValidator).isValidating();
         mPhoneSwitcher.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
-        waitABit();
+        processAllMessages();
         // Call 1 should be cancelled and failed. Call 2 return success immediately as there's no
         // change.
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
         verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
-        mHandlerThread.quit();
     }
 
     /* Private utility methods start here */
@@ -995,25 +920,25 @@
     private void notifyPhoneAsInCall(Phone phone) {
         doReturn(mActiveCall).when(phone).getForegroundCall();
         mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
-        waitABit();
+        processAllMessages();
     }
 
     private void notifyPhoneAsInHoldingCall(Phone phone) {
         doReturn(mHoldingCall).when(phone).getBackgroundCall();
         mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
-        waitABit();
+        processAllMessages();
     }
 
     private void notifyPhoneAsInactive(Phone phone) {
         doReturn(mInactiveCall).when(phone).getForegroundCall();
         mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
-        waitABit();
+        processAllMessages();
     }
 
     private void notifyDataEnabled(boolean dataEnabled) {
         doReturn(dataEnabled).when(mDataEnabledSettings).isDataEnabled(anyInt());
         mPhoneSwitcher.sendEmptyMessage(EVENT_DATA_ENABLED_CHANGED);
-        waitABit();
+        processAllMessages();
     }
 
     private Message getEcbmRegistration(Phone phone) {
@@ -1032,25 +957,25 @@
         doReturn(mInactiveCall).when(phone).getForegroundCall();
         doReturn(true).when(phone).isInEcm();
         ecmMessage.sendToTarget();
-        waitABit();
+        processAllMessages();
     }
 
     private void notifyEcbmEnd(Phone phone, Message ecmMessage) {
         doReturn(false).when(phone).isInEcm();
         ecmMessage.sendToTarget();
-        waitABit();
+        processAllMessages();
     }
 
     private void sendPreferredDataSuccessResult(int phoneId) {
         // make sure the radio command is called and then send a success result
+        processAllMessages();
         ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
-        verify(mMockRadioConfig, timeout(500)).setPreferredDataModem(eq(phoneId),
-                msgCaptor.capture());
+        verify(mMockRadioConfig).setPreferredDataModem(eq(phoneId), msgCaptor.capture());
         assertNotNull(msgCaptor.getValue());
         // Send back successful result
         AsyncResult.forMessage(msgCaptor.getValue(), null, null);
         msgCaptor.getValue().sendToTarget();
-        waitABit();
+        processAllMessages();
     }
 
     private void setMsimDefaultDataSubId(int numPhones, int defaultDataSub) throws Exception {
@@ -1058,9 +983,7 @@
             setSlotIndexToSubId(i, i + 1);
         }
         setDefaultDataSubId(defaultDataSub);
-        waitABit();
         NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
-        waitABit();
         for (int i = 0; i < numPhones; i++) {
             if (defaultDataSub == (i + 1)) {
                 // sub id is always phoneId+1 for testing
@@ -1074,6 +997,7 @@
     private void sendDefaultDataSubChanged() {
         final Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
         mContext.sendBroadcast(intent);
+        processAllMessages();
     }
 
     private void initialize(int numPhones, int maxActivePhones) throws Exception {
@@ -1087,17 +1011,10 @@
         initializeTelRegistryMock();
         initializeConnManagerMock();
 
-        mHandlerThread = new HandlerThread("PhoneSwitcherTestThread") {
-            @Override
-            public void onLooperPrepared() {
-                mPhoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
-                        mContext, mSubscriptionController, this.getLooper(),
-                        mTelRegistryMock, mCommandsInterfaces, mPhones);
-            }
-        };
-
-        mHandlerThread.start();
-        waitABit();
+        mPhoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
+                mContext, mSubscriptionController, Looper.myLooper(),
+                mTelRegistryMock, mCommandsInterfaces, mPhones);
+        processAllMessages();
 
         verify(mTelRegistryMock).addOnSubscriptionsChangedListener(
                 eq(mContext.getOpPackageName()), any());
@@ -1247,6 +1164,7 @@
         message.arg1 = score;
         message.obj = networkRequest;
         mNetworkFactoryMessenger.send(message);
+        processAllMessages();
 
         return networkRequest;
     }
@@ -1271,6 +1189,7 @@
         message.arg1 = 50; // Score
         message.obj = networkRequest;
         mNetworkFactoryMessenger.send(message);
+        processAllMessages();
 
         return networkRequest;
     }
@@ -1283,12 +1202,6 @@
         message.what = android.net.NetworkFactory.CMD_CANCEL_REQUEST;
         message.obj = networkRequest;
         mNetworkFactoryMessenger.send(message);
+        processAllMessages();
     }
-
-    private void waitABit() {
-        try {
-            Thread.sleep(250);
-        } catch (Exception e) {
-        }
-    }
-}
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index e823906..aa7335c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -44,6 +44,8 @@
 import android.os.IBinder;
 import android.os.IDeviceIdleController;
 import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
 import android.os.RegistrantList;
 import android.os.ServiceManager;
 import android.provider.BlockedNumberContract;
@@ -59,6 +61,7 @@
 import android.telephony.ims.ImsCallProfile;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
+import android.testing.TestableLooper;
 import android.util.Log;
 import android.util.Singleton;
 
@@ -100,6 +103,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -115,6 +119,23 @@
             new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
             EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
 
+    private static final Field MESSAGE_QUEUE_FIELD;
+    private static final Field MESSAGE_WHEN_FIELD;
+    private static final Field MESSAGE_NEXT_FIELD;
+
+    static {
+        try {
+            MESSAGE_QUEUE_FIELD = MessageQueue.class.getDeclaredField("mMessages");
+            MESSAGE_QUEUE_FIELD.setAccessible(true);
+            MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
+            MESSAGE_WHEN_FIELD.setAccessible(true);
+            MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
+            MESSAGE_NEXT_FIELD.setAccessible(true);
+        } catch (NoSuchFieldException e) {
+            throw new RuntimeException("Failed to initialize TelephonyTest", e);
+        }
+    }
+
     @Mock
     protected GsmCdmaPhone mPhone;
     @Mock
@@ -259,7 +280,8 @@
     private boolean mReady;
     protected HashMap<String, IBinder> mServiceManagerMockedServices = new HashMap<>();
     protected Phone[] mPhones;
-
+    protected List<TestableLooper> mTestableLoopers = new ArrayList<>();
+    protected TestableLooper mTestableLooper;
 
     protected HashMap<Integer, ImsManager> mImsManagerInstances = new HashMap<>();
     private HashMap<InstanceKey, Object> mOldInstances = new HashMap<InstanceKey, Object>();
@@ -588,12 +610,24 @@
         assertNotNull("Failed to set up SubscriptionController singleton",
                 SubscriptionController.getInstance());
         setReady(false);
+        // create default TestableLooper for test and add to list of monitored loopers
+        mTestableLooper = TestableLooper.get(TelephonyTest.this);
+        if (mTestableLooper != null) {
+            monitorTestableLooper(mTestableLooper);
+        }
     }
 
     protected void tearDown() throws Exception {
-
+        // unmonitor TestableLooper
+        if (mTestableLooper != null) {
+            unmonitorTestableLooper(mTestableLooper);
+        }
         mSimulatedCommands.dispose();
 
+        // destroy all created TestableLoopers so they can be reused
+        for (TestableLooper looper : mTestableLoopers) {
+            looper.destroy();
+        }
         SharedPreferences sharedPreferences = mContext.getSharedPreferences((String) null, 0);
         sharedPreferences.edit().clear().commit();
 
@@ -762,4 +796,74 @@
         }
         return null;
     }
+
+    /**
+     * Add a TestableLooper to the list of monitored loopers
+     * @param looper added if it doesn't already exist
+     */
+    public void monitorTestableLooper(TestableLooper looper) {
+        if (!mTestableLoopers.contains(looper)) {
+            mTestableLoopers.add(looper);
+        }
+    }
+
+    /**
+     * Remove a TestableLooper from the list of monitored loopers
+     * @param looper removed if it does exist
+     */
+    public void unmonitorTestableLooper(TestableLooper looper) {
+        if (mTestableLoopers.contains(looper)) {
+            mTestableLoopers.remove(looper);
+        }
+    }
+
+    /**
+     * Handle all messages that can be processed at the current time
+     * for all monitored TestableLoopers
+     */
+    public void processAllMessages() {
+        if (mTestableLoopers.isEmpty()) {
+            fail("mTestableLoopers is empty. Please make sure to add @RunWithLooper annotation");
+        }
+        while (!areAllTestableLoopersIdle()) {
+            for (TestableLooper looper : mTestableLoopers) looper.processAllMessages();
+        }
+    }
+
+    /**
+     * Check if there are any messages to be processed in any monitored TestableLooper
+     * Delayed messages to be handled at a later time will be ignored
+     * @return true if there are no messages that can be handled at the current time
+     *         across all monitored TestableLoopers
+     */
+    private boolean areAllTestableLoopersIdle() {
+        for (TestableLooper looper : mTestableLoopers) {
+            if (!looper.getLooper().getQueue().isIdle()) return false;
+        }
+        return true;
+    }
+
+    /**
+     * Effectively moves time forward by reducing the time of all messages
+     * for all monitored TestableLoopers
+     * @param milliSeconds number of milliseconds to move time forward by
+     */
+    public void moveTimeForward(long milliSeconds) {
+        for (TestableLooper looper : mTestableLoopers) {
+            MessageQueue queue = looper.getLooper().getQueue();
+            try {
+                Message msg = (Message) MESSAGE_QUEUE_FIELD.get(queue);
+                while (msg != null) {
+                    long updatedWhen = msg.getWhen() - milliSeconds;
+                    if (updatedWhen < 0) {
+                        updatedWhen = 0;
+                    }
+                    MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
+                    msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
+                }
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException("Access failed in TelephonyTest", e);
+            }
+        }
+    }
 }