SlicePurchaseController only unregister receiver if it exists

When entitlement checks fail, we throttle the request before the
broadcast receiver has been created/registered and before creating a
purchase timeout. unregister the broadcast receiver and remove the timer
only if it has been created/exists to prevent a NPE.
Update unit tests to actually send the broadcast and capture the
broadcast receiver.

Test: manual
Test: atest SlicePurchaseControllerTest
Bug: 257317287
Change-Id: Ie7dfda4d62cebc8aad94afc0c91547a9aed11b5a
diff --git a/src/com/android/phone/slice/SlicePurchaseController.java b/src/com/android/phone/slice/SlicePurchaseController.java
index 5c3b63e..f40bb69 100644
--- a/src/com/android/phone/slice/SlicePurchaseController.java
+++ b/src/com/android/phone/slice/SlicePurchaseController.java
@@ -258,7 +258,11 @@
     /** Premium network entitlement query API */
     @NonNull private final PremiumNetworkEntitlementApi mPremiumNetworkEntitlementApi;
 
-    private class SlicePurchaseControllerBroadcastReceiver extends BroadcastReceiver {
+    /**
+     * BroadcastReceiver to receive responses from the slice purchase application.
+     */
+    @VisibleForTesting
+    public class SlicePurchaseControllerBroadcastReceiver extends BroadcastReceiver {
         @TelephonyManager.PremiumCapability private final int mCapability;
 
         SlicePurchaseControllerBroadcastReceiver(
@@ -369,6 +373,11 @@
         return sInstances.get(phoneId);
     }
 
+    /**
+     * Create a SlicePurchaseController for the given phone on the given looper.
+     * @param phone The Phone to create the SlicePurchaseController for.
+     * @param looper The Looper to run the SlicePurchaseController on.
+     */
     @VisibleForTesting
     public SlicePurchaseController(@NonNull Phone phone, @NonNull Looper looper) {
         super(looper);
@@ -537,8 +546,11 @@
     private void handlePurchaseResult(
             @TelephonyManager.PremiumCapability int capability,
             @TelephonyManager.PurchasePremiumCapabilityResult int result, boolean throttle) {
-        mPhone.getContext().unregisterReceiver(
-                mSlicePurchaseControllerBroadcastReceivers.remove(capability));
+        SlicePurchaseControllerBroadcastReceiver receiver =
+                mSlicePurchaseControllerBroadcastReceivers.remove(capability);
+        if (receiver != null) {
+            mPhone.getContext().unregisterReceiver(receiver);
+        }
         removeMessages(EVENT_PURCHASE_TIMEOUT, capability);
         if (throttle) {
             throttleCapability(capability, getThrottleDuration(result));
@@ -581,7 +593,7 @@
         PremiumNetworkEntitlementResponse premiumNetworkEntitlementResponse =
                 mPremiumNetworkEntitlementApi.checkEntitlementStatus(capability);
 
-        /* invalid response for entitlement check */
+        // invalid response for entitlement check
         if (premiumNetworkEntitlementResponse == null) {
             logd("Invalid response for entitlement check.");
             handlePurchaseResult(capability,
@@ -660,7 +672,8 @@
      *                {@code false} if it should be immutable.
      * @return The PendingIntent for the given action and capability.
      */
-    @NonNull private PendingIntent createPendingIntent(@NonNull String action,
+    @VisibleForTesting
+    @NonNull public PendingIntent createPendingIntent(@NonNull String action,
             @TelephonyManager.PremiumCapability int capability, boolean mutable) {
         Intent intent = new Intent(action);
         intent.putExtra(EXTRA_PHONE_ID, mPhone.getPhoneId());
diff --git a/tests/src/com/android/phone/SlicePurchaseControllerTest.java b/tests/src/com/android/phone/SlicePurchaseControllerTest.java
index bfde6a4..260bd91 100644
--- a/tests/src/com/android/phone/SlicePurchaseControllerTest.java
+++ b/tests/src/com/android/phone/SlicePurchaseControllerTest.java
@@ -20,12 +20,21 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -38,7 +47,6 @@
 import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.NetworkSlicingConfig;
 import android.testing.TestableLooper;
-import android.util.Log;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
@@ -48,14 +56,14 @@
 import com.android.phone.slice.PremiumNetworkEntitlementApi;
 import com.android.phone.slice.PremiumNetworkEntitlementResponse;
 import com.android.phone.slice.SlicePurchaseController;
+import com.android.phone.slice.SlicePurchaseController.SlicePurchaseControllerBroadcastReceiver;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
 import java.util.Collections;
 import java.util.Map;
 
@@ -77,6 +85,7 @@
     @Mock PremiumNetworkEntitlementApi mPremiumNetworkEntitlementApi;
 
     private SlicePurchaseController mSlicePurchaseController;
+    private SlicePurchaseControllerBroadcastReceiver mBroadcastReceiver;
     private PersistableBundle mBundle;
     private PremiumNetworkEntitlementResponse mEntitlementResponse;
     private Handler mHandler;
@@ -109,7 +118,12 @@
         mBundle = new PersistableBundle();
         doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
 
-        mSlicePurchaseController = new SlicePurchaseController(mPhone, mHandler.getLooper());
+        // create a spy to mock final PendingIntent methods
+        SlicePurchaseController slicePurchaseController =
+                new SlicePurchaseController(mPhone, mHandler.getLooper());
+        mSlicePurchaseController = spy(slicePurchaseController);
+        doReturn(null).when(mSlicePurchaseController).createPendingIntent(
+                anyString(), anyInt(), anyBoolean());
         replaceInstance(SlicePurchaseController.class, "sInstances", mSlicePurchaseController,
                 Map.of(PHONE_ID, mSlicePurchaseController));
         replaceInstance(SlicePurchaseController.class, "mPremiumNetworkEntitlementApi",
@@ -120,6 +134,21 @@
     }
 
     @Test
+    public void testCreatePendingIntent() {
+        doCallRealMethod().when(mSlicePurchaseController).createPendingIntent(
+                anyString(), anyInt(), anyBoolean());
+        try {
+            mSlicePurchaseController.createPendingIntent(
+                    "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_CANCELED",
+                    TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY,
+                    true);
+        } catch (Exception expected) {
+            return;
+        }
+        fail("Expected createPendingIntent to throw an exception");
+    }
+
+    @Test
     public void testIsPremiumCapabilityAvailableForPurchase() {
         assertFalse(mSlicePurchaseController.isPremiumCapabilityAvailableForPurchase(
                 TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY));
@@ -140,7 +169,10 @@
 
     @Test
     public void testPurchasePremiumCapabilityResultFeatureNotSupported() {
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
                 mResult);
 
@@ -148,7 +180,10 @@
         doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
                 .getCachedAllowedNetworkTypesBitmask();
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
                 mResult);
     }
@@ -158,7 +193,10 @@
         doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
                 .getCachedAllowedNetworkTypesBitmask();
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED, mResult);
 
         // retry after enabling carrier configs
@@ -167,7 +205,10 @@
         mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, URL);
         doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
                 mResult);
     }
@@ -181,7 +222,10 @@
         mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, URL);
         doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(
                 TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
                 mResult);
@@ -189,7 +233,10 @@
         // retry on default data subscription
         doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertNotEquals(
                 TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
                 mResult);
@@ -205,20 +252,26 @@
         doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
         doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
                 mResult);
 
         // retry with valid network
         doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mServiceState).getDataNetworkType();
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
                 mResult);
     }
 
     @Test
-    public void testPurchasePremiumCapabilityResultEntitlementCheckFailed() throws Exception {
+    public void testPurchasePremiumCapabilityResultEntitlementCheckFailed() {
         doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
                 .getCachedAllowedNetworkTypesBitmask();
         mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
@@ -229,7 +282,10 @@
         doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mServiceState).getDataNetworkType();
         doReturn(null).when(mPremiumNetworkEntitlementApi).checkEntitlementStatus(anyInt());
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED,
                 mResult);
 
@@ -239,7 +295,10 @@
         doReturn(mEntitlementResponse).when(mPremiumNetworkEntitlementApi)
                 .checkEntitlementStatus(anyInt());
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
                 mResult);
 
@@ -249,7 +308,10 @@
         doReturn(mEntitlementResponse).when(mPremiumNetworkEntitlementApi)
                 .checkEntitlementStatus(anyInt());
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
                 mResult);
 
@@ -265,12 +327,18 @@
                 PURCHASE_CONDITION_TIMEOUT);
         doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED,
                 mResult);
 
         // retry to verify throttled
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
 
         // retry with valid entitlement check to verify unthrottled
@@ -284,18 +352,24 @@
     public void testPurchasePremiumCapabilityResultAlreadyInProgress() {
         sendValidPurchaseRequest();
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
                 mResult);
 
         // retry to verify same result
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
                 mResult);
     }
 
     @Test
-    public void testPurchasePremiumCapabilityResultSuccess() throws Exception {
+    public void testPurchasePremiumCapabilityResultSuccess() {
         sendValidPurchaseRequest();
 
         Intent intent = new Intent();
@@ -303,17 +377,21 @@
         intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
         intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
                 TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
-        receiveSlicePurchaseResponse(intent);
+        mBroadcastReceiver.onReceive(mMockedContext, intent);
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS, mResult);
 
         // retry tested in testPurchasePremiumCapabilityResultPendingNetworkSetup
     }
 
     @Test
-    public void testPurchasePremiumCapabilityResultPendingNetworkSetup() throws Exception {
+    public void testPurchasePremiumCapabilityResultPendingNetworkSetup() {
         testPurchasePremiumCapabilityResultSuccess();
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP,
                 mResult);
 
@@ -325,7 +403,7 @@
     }
 
     @Test
-    public void testPurchasePremiumCapabilityResultAlreadyPurchased() throws Exception {
+    public void testPurchasePremiumCapabilityResultAlreadyPurchased() {
         testPurchasePremiumCapabilityResultSuccess();
 
         // TODO: implement slicing config logic properly
@@ -336,12 +414,18 @@
                 new AsyncResult(null, slicingConfig, null)).sendToTarget();
         mTestableLooper.processAllMessages();
 
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
                 mResult);
 
         // retry to verify same result
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
                 mResult);
 
@@ -355,7 +439,7 @@
     }
 
     @Test
-    public void testPurchasePremiumCapabilityResultTimeout() throws Exception {
+    public void testPurchasePremiumCapabilityResultTimeout() {
         sendValidPurchaseRequest();
 
         mTestableLooper.moveTimeForward(NOTIFICATION_TIMEOUT);
@@ -363,7 +447,10 @@
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT, mResult);
 
         // retry to verify throttled
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
 
         // retry to verify unthrottled
@@ -374,7 +461,7 @@
     }
 
     @Test
-    public void testPurchasePremiumCapabilityResultUserCanceled() throws Exception {
+    public void testPurchasePremiumCapabilityResultUserCanceled() {
         sendValidPurchaseRequest();
 
         Intent intent = new Intent();
@@ -382,11 +469,15 @@
         intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
         intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
                 TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
-        receiveSlicePurchaseResponse(intent);
+        mBroadcastReceiver.onReceive(mMockedContext, intent);
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED, mResult);
 
         // retry to verify throttled
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
 
         // retry to verify unthrottled
@@ -397,7 +488,7 @@
     }
 
     @Test
-    public void testPurchasePremiumCapabilityResultCarrierError() throws Exception {
+    public void testPurchasePremiumCapabilityResultCarrierError() {
         sendValidPurchaseRequest();
 
         Intent intent = new Intent();
@@ -408,11 +499,15 @@
                 TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
         intent.putExtra(SlicePurchaseController.EXTRA_FAILURE_CODE,
                 SlicePurchaseController.FAILURE_CODE_SERVER_UNREACHABLE);
-        receiveSlicePurchaseResponse(intent);
+        mBroadcastReceiver.onReceive(mMockedContext, intent);
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, mResult);
 
         // retry to verify throttled
-        tryPurchasePremiumCapability();
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
 
         // retry to verify unthrottled
@@ -423,7 +518,7 @@
     }
 
     @Test
-    public void testPurchasePremiumCapabilityResultRequestFailed() throws Exception {
+    public void testPurchasePremiumCapabilityResultRequestFailed() {
         sendValidPurchaseRequest();
 
         Intent intent = new Intent();
@@ -432,7 +527,8 @@
         intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
         intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
                 TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
-        receiveSlicePurchaseResponse(intent);
+        mBroadcastReceiver.onReceive(mMockedContext, intent);
+        mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED, mResult);
 
         // retry to verify no throttling
@@ -440,8 +536,7 @@
     }
 
     @Test
-    public void testPurchasePremiumCapabilityResultNotDefaultDataSubscriptionResponse()
-            throws Exception {
+    public void testPurchasePremiumCapabilityResultNotDefaultDataSubscriptionResponse() {
         sendValidPurchaseRequest();
 
         Intent intent = new Intent();
@@ -450,7 +545,8 @@
         intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
         intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
                 TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
-        receiveSlicePurchaseResponse(intent);
+        mBroadcastReceiver.onReceive(mMockedContext, intent);
+        mTestableLooper.processAllMessages();
         assertEquals(
                 TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
                 mResult);
@@ -460,6 +556,8 @@
     }
 
     private void sendValidPurchaseRequest() {
+        clearInvocations(mMockedContext);
+
         // feature supported
         doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
                 .getCachedAllowedNetworkTypesBitmask();
@@ -489,37 +587,25 @@
         doReturn(mEntitlementResponse).when(mPremiumNetworkEntitlementApi)
                 .checkEntitlementStatus(anyInt());
 
-        tryPurchasePremiumCapability();
-        // verify that the purchase request was sent successfully and purchase timeout started
+        // send purchase request
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
+
+        // verify that the purchase request was sent successfully
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mMockedContext).sendBroadcast(intentCaptor.capture());
+        Intent intent = intentCaptor.getValue();
+        assertEquals(SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP, intent.getAction());
         assertTrue(mSlicePurchaseController.hasMessages(4 /* EVENT_PURCHASE_TIMEOUT */,
                 TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY));
-    }
 
-    private void tryPurchasePremiumCapability() {
-        try {
-            mSlicePurchaseController.purchasePremiumCapability(
-                    TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
-                    mHandler.obtainMessage());
-            mTestableLooper.processAllMessages();
-        } catch (Exception expected) {
-            // SecurityException rethrown as RuntimeException when broadcasting intent
-            Log.d(TAG, expected.getMessage());
-        }
-        mTestableLooper.processAllMessages();
-    }
-
-    private void receiveSlicePurchaseResponse(Intent intent) throws Exception {
-        Class<?> receiverClass = Class.forName("com.android.phone.slice.SlicePurchaseController"
-                + "$SlicePurchaseControllerBroadcastReceiver");
-        Constructor<?> constructor = receiverClass.getDeclaredConstructor(
-                SlicePurchaseController.class, int.class);
-        constructor.setAccessible(true);
-        Object receiver = constructor.newInstance(mSlicePurchaseController,
-                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
-
-        Method method = receiver.getClass().getDeclaredMethod(
-                "onReceive", Context.class, Intent.class);
-        method.invoke(receiver, mMockedContext, intent);
-        mTestableLooper.processAllMessages();
+        // capture the broadcast receiver to fake responses from the slice purchase application
+        ArgumentCaptor<SlicePurchaseControllerBroadcastReceiver> broadcastReceiverCaptor =
+                ArgumentCaptor.forClass(SlicePurchaseControllerBroadcastReceiver.class);
+        verify(mMockedContext).registerReceiver(
+                broadcastReceiverCaptor.capture(), any(IntentFilter.class));
+        mBroadcastReceiver = broadcastReceiverCaptor.getValue();
     }
 }