[ImsServiceEntitlement] execute handleInitialEntitlementStatus and handleReevaluationEntitlementStatus on the main thread

Error:
RuntimeException  Can't create handler inside thread Thread[AsyncTask #1,5,main] that has not called Looper.prepare()

Solution:
1. Execute the callback (handleInitialEntitlementStatus or handleReevaluationEntitlementStatus) in onSuccess() on the main thread, even though onSuccess() runs on the async thread
2. Use a handler to post delayed events instead of using CountDownTimer

bug: 399230129
bug: 398184731

Change-Id: I2aa58c22a6c770a0d96cccb637b2f244c8dd8629
diff --git a/src/com/android/imsserviceentitlement/WfcActivationController.java b/src/com/android/imsserviceentitlement/WfcActivationController.java
index 4b65e46..ccf6015 100644
--- a/src/com/android/imsserviceentitlement/WfcActivationController.java
+++ b/src/com/android/imsserviceentitlement/WfcActivationController.java
@@ -31,7 +31,8 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.Looper;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -70,6 +71,7 @@
     private final Intent mStartIntent;
     private final MetricsLogger mMetricsLogger;
     private final Context mContext;
+    private final Handler mMainThreadHandler;
 
     // States
     private int mEvaluateTimes = 0;
@@ -88,6 +90,7 @@
         this.mTelephonyUtils = new TelephonyUtils(context, getSubId());
         this.mImsUtils = ImsUtils.getInstance(context, getSubId());
         this.mMetricsLogger = new MetricsLogger(mTelephonyUtils);
+        this.mMainThreadHandler = new Handler(Looper.getMainLooper());
     }
 
     @VisibleForTesting
@@ -97,7 +100,8 @@
             ImsEntitlementApi imsEntitlementApi,
             Intent intent,
             ImsUtils imsUtils,
-            MetricsLogger metricsLogger) {
+            MetricsLogger metricsLogger,
+            Handler handler) {
         this.mContext = context;
         this.mStartIntent = intent;
         this.mActivationUi = wfcActivationUi;
@@ -105,6 +109,7 @@
         this.mTelephonyUtils = new TelephonyUtils(context, getSubId());
         this.mImsUtils = imsUtils;
         this.mMetricsLogger = metricsLogger;
+        this.mMainThreadHandler = handler;
     }
 
     /** Indicates the controller to start WFC activation or emergency address update flow. */
@@ -127,7 +132,9 @@
             return;
         }
         EntitlementUtils.entitlementCheck(
-                mImsEntitlementApi, result -> handleInitialEntitlementStatus(result));
+                mImsEntitlementApi,
+                result -> mMainThreadHandler.post(
+                        () -> handleInitialEntitlementStatus(result)));
     }
 
     /**
@@ -144,7 +151,9 @@
     @MainThread
     public void reevaluateEntitlementStatus() {
         EntitlementUtils.entitlementCheck(
-                mImsEntitlementApi, result -> handleReevaluationEntitlementStatus(result));
+                mImsEntitlementApi,
+                result -> mMainThreadHandler.post(
+                        () -> handleReevaluationEntitlementStatus(result)));
     }
 
     /** The interface for handling the entitlement check result. */
@@ -314,9 +323,9 @@
                 // Check again after 5s, max retry 6 times
                 if (mEvaluateTimes < ENTITLEMENT_STATUS_UPDATE_RETRY_MAX) {
                     mEvaluateTimes += 1;
-                    postDelay(
-                            getEntitlementStatusUpdateRetryIntervalMs(),
-                            this::reevaluateEntitlementStatus);
+                    mMainThreadHandler.postDelayed(
+                            this::reevaluateEntitlementStatus,
+                            getEntitlementStatusUpdateRetryIntervalMs());
                 } else {
                     mEvaluateTimes = 0;
                     showGeneralErrorUi();
@@ -359,22 +368,6 @@
         }
     }
 
-    /** Runs {@code action} on caller's thread after {@code delayMillis} ms. */
-    private static void postDelay(long delayMillis, Runnable action) {
-        new CountDownTimer(delayMillis, delayMillis + 100) {
-            // Use a countDownInterval bigger than millisInFuture so onTick never fires.
-            @Override
-            public void onTick(long millisUntilFinished) {
-                // Do nothing
-            }
-
-            @Override
-            public void onFinish() {
-                action.run();
-            }
-        }.start();
-    }
-
     private void finishStatsLog(int result) {
         mAppResult = result;
     }
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java b/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java
index b69127c..8599f5c 100644
--- a/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java
+++ b/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java
@@ -36,11 +36,16 @@
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.os.PersistableBundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.TestLooperManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
 import androidx.test.core.app.ApplicationProvider;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.imsserviceentitlement.entitlement.EntitlementResult;
@@ -53,6 +58,7 @@
 import com.android.imsserviceentitlement.utils.ImsUtils;
 import com.android.imsserviceentitlement.utils.MetricsLogger;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -86,11 +92,21 @@
     private WfcActivationController mWfcActivationController;
     private Context mContext;
     private PersistableBundle mCarrierConfig;
+    private TestLooperManager mTestLooperManager;
+    private Handler mUiHandler;
+    private HandlerThread mUiHandlerThread;
 
     @Before
     public void setUp() throws Exception {
         mContext = spy(ApplicationProvider.getApplicationContext());
 
+        mUiHandlerThread = new HandlerThread("MockUiThread");
+        mUiHandlerThread.start();
+        mUiHandler = new Handler(mUiHandlerThread.getLooper());
+        mTestLooperManager =
+                InstrumentationRegistry.getInstrumentation()
+                        .acquireLooperManager(mUiHandlerThread.getLooper());
+
         when(mContext.getSystemService(CarrierConfigManager.class))
                 .thenReturn(mMockCarrierConfigManager);
         when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mMockTelephonyManager);
@@ -104,6 +120,12 @@
         field.set(null, true);
     }
 
+    @After
+    public void tearDown() {
+        mTestLooperManager.release();
+        mUiHandlerThread.quit();
+    }
+
     @Test
     public void startFlow_launchAppForActivation_setPurposeActivation() {
         InOrder mOrderVerifier = inOrder(mMockActivationUi);
@@ -149,18 +171,7 @@
     @Test
     public void finish_launchAppForActivateWithIsSkipWfcActivationTrue_notWriteMetrics() {
         setIsSkipWfcActivation(true);
-        Intent startIntent = new Intent(Intent.ACTION_MAIN);
-        startIntent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
-        startIntent.putExtra(
-                ActivityConstants.EXTRA_LAUNCH_CARRIER_APP, ActivityConstants.LAUNCH_APP_ACTIVATE);
-        mWfcActivationController =
-                new WfcActivationController(
-                        mContext,
-                        mMockActivationUi,
-                        mMockActivationApi,
-                        startIntent,
-                        mMockImsUtils,
-                        mMockMetricsLogger);
+        buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.finish();
 
@@ -170,18 +181,7 @@
     @Test
     public void finish_launchAppForUpdateWithIsSkipWfcActivationTrue_writeMetrics() {
         setIsSkipWfcActivation(true);
-        Intent startIntent = new Intent(Intent.ACTION_MAIN);
-        startIntent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
-        startIntent.putExtra(
-                ActivityConstants.EXTRA_LAUNCH_CARRIER_APP, ActivityConstants.LAUNCH_APP_UPDATE);
-        mWfcActivationController =
-                new WfcActivationController(
-                        mContext,
-                        mMockActivationUi,
-                        mMockActivationApi,
-                        startIntent,
-                        mMockImsUtils,
-                        mMockMetricsLogger);
+        buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
 
         mWfcActivationController.finish();
 
@@ -190,18 +190,7 @@
 
     @Test
     public void finish_launchAppForUpdateAndIsSkipWfcActivationFalse_writeMetrics() {
-        Intent startIntent = new Intent(Intent.ACTION_MAIN);
-        startIntent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
-        startIntent.putExtra(
-                ActivityConstants.EXTRA_LAUNCH_CARRIER_APP, ActivityConstants.LAUNCH_APP_UPDATE);
-        mWfcActivationController =
-                new WfcActivationController(
-                        mContext,
-                        mMockActivationUi,
-                        mMockActivationApi,
-                        startIntent,
-                        mMockImsUtils,
-                        mMockMetricsLogger);
+        buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
 
         mWfcActivationController.finish();
 
@@ -215,6 +204,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.finishFlow();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         mOrderVerifier
                 .verify(mMockActivationUi)
@@ -250,20 +240,10 @@
                                                 .build())
                                 .build());
         setNetworkConnected(false);
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
-        intent.putExtra(ActivityConstants.EXTRA_LAUNCH_CARRIER_APP,
-                ActivityConstants.LAUNCH_APP_UPDATE);
-        mWfcActivationController =
-                new WfcActivationController(
-                        mContext,
-                        mMockActivationUi,
-                        mMockActivationApi,
-                        null,
-                        mMockImsUtils,
-                        mMockMetricsLogger);
+        buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
 
         mWfcActivationController.finishFlow();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verify(mMockActivationUi).setResultAndFinish(eq(Activity.RESULT_OK));
     }
@@ -272,20 +252,10 @@
     public void finish_startFlowForActivate_writeLoggerPurposeActivation() {
         when(mMockTelephonyManager.getSimCarrierId()).thenReturn(CARRIER_ID);
         when(mMockTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
-        intent.putExtra(ActivityConstants.EXTRA_LAUNCH_CARRIER_APP,
-                ActivityConstants.LAUNCH_APP_ACTIVATE);
-        mWfcActivationController =
-                new WfcActivationController(
-                        mContext,
-                        mMockActivationUi,
-                        mMockActivationApi,
-                        intent,
-                        mMockImsUtils,
-                        mMockMetricsLogger);
+        buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.startFlow();
+        mTestLooperManager.execute(mTestLooperManager.next());
         mWfcActivationController.finish();
 
         verify(mMockMetricsLogger).start(eq(IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__ACTIVATION));
@@ -298,10 +268,6 @@
     public void finish_entitlementResultWfcEntitled_writeLoggerAppResultSuccessful() {
         when(mMockTelephonyManager.getSimCarrierId()).thenReturn(CARRIER_ID);
         when(mMockTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
-        intent.putExtra(ActivityConstants.EXTRA_LAUNCH_CARRIER_APP,
-                ActivityConstants.LAUNCH_APP_ACTIVATE);
         when(mMockActivationApi.checkEntitlementStatus())
                 .thenReturn(
                         EntitlementResult.builder(false)
@@ -313,16 +279,10 @@
                                                 .setAddrStatus(AddrStatus.AVAILABLE)
                                                 .build())
                                 .build());
-        mWfcActivationController =
-                new WfcActivationController(
-                        mContext,
-                        mMockActivationUi,
-                        mMockActivationApi,
-                        intent,
-                        mMockImsUtils,
-                        mMockMetricsLogger);
+        buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.startFlow();
+        mTestLooperManager.execute(mTestLooperManager.next());
         mWfcActivationController.finish();
 
         verify(mMockMetricsLogger).start(eq(IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__ACTIVATION));
@@ -347,6 +307,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verify(mMockActivationUi).setResultAndFinish(Activity.RESULT_OK);
     }
@@ -368,6 +329,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verify(mMockActivationUi).showWebview(EMERGENCY_ADDRESS_WEB_URL,
                 EMERGENCY_ADDRESS_WEB_DATA);
@@ -389,6 +351,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verify(mMockActivationUi).showWebview(EMERGENCY_ADDRESS_WEB_URL, null);
     }
@@ -406,6 +369,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verifyErrorUi(R.string.activate_title, R.string.failure_contact_carrier);
     }
@@ -425,6 +389,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verifyErrorUi(R.string.activate_title, R.string.wfc_activation_error);
     }
@@ -445,6 +410,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.reevaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verify(mMockActivationUi).setResultAndFinish(Activity.RESULT_OK);
     }
@@ -464,6 +430,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.reevaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verifyErrorUi(R.string.activate_title, R.string.wfc_activation_error);
     }
@@ -484,6 +451,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
 
         mWfcActivationController.reevaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verify(mMockActivationUi).setResultAndFinish(eq(Activity.RESULT_OK));
     }
@@ -500,20 +468,10 @@
                                         .build())
                         .build();
         when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
-        intent.putExtra(ActivityConstants.EXTRA_LAUNCH_CARRIER_APP,
-                ActivityConstants.LAUNCH_APP_UPDATE);
-        mWfcActivationController =
-                new WfcActivationController(
-                        mContext,
-                        mMockActivationUi,
-                        mMockActivationApi,
-                        intent,
-                        mMockImsUtils,
-                        mMockMetricsLogger);
+        buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
 
         mWfcActivationController.reevaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verify(mMockImsUtils).turnOffWfc(any());
     }
@@ -533,6 +491,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
 
         mWfcActivationController.reevaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verifyErrorUi(R.string.e911_title, R.string.address_update_error);
     }
@@ -555,6 +514,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verify(mMockActivationUi).showWebview(EMERGENCY_ADDRESS_WEB_URL,
                 EMERGENCY_ADDRESS_WEB_DATA);
@@ -577,6 +537,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_SHOW_TC);
 
         mWfcActivationController.evaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verify(mMockActivationUi).showWebview(EMERGENCY_ADDRESS_WEB_URL, null);
     }
@@ -594,6 +555,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verifyErrorUi(R.string.e911_title, R.string.failure_contact_carrier);
     }
@@ -611,6 +573,7 @@
         buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
+        mTestLooperManager.execute(mTestLooperManager.next());
 
         verifyErrorUi(R.string.e911_title, R.string.address_update_error);
     }
@@ -620,8 +583,14 @@
         intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
         intent.putExtra(ActivityConstants.EXTRA_LAUNCH_CARRIER_APP, extraLaunchCarrierApp);
         mWfcActivationController =
-                new WfcActivationController(mContext, mMockActivationUi, mMockActivationApi,
-                        intent);
+                new WfcActivationController(
+                        mContext,
+                        mMockActivationUi,
+                        mMockActivationApi,
+                        intent,
+                        mMockImsUtils,
+                        mMockMetricsLogger,
+                        mUiHandler);
     }
 
     private void setNetworkConnected(boolean isConnected) {