Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 785c6ecf authored by Jeff Davidson's avatar Jeff Davidson Committed by Android (Google) Code Review
Browse files

Merge "Allow carrier-privileged apps to download profiles."

parents ae4104b0 08d3d312
Loading
Loading
Loading
Loading
+183 −93
Original line number Diff line number Diff line
@@ -17,15 +17,18 @@ package com.android.internal.telephony.euicc;

import android.Manifest;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.ServiceManager;
import android.service.euicc.DownloadResult;
import android.service.euicc.GetDownloadableSubscriptionMetadataResult;
import android.telephony.TelephonyManager;
import android.telephony.UiccAccessRule;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccManager;
import android.util.Log;
@@ -41,10 +44,19 @@ import java.util.concurrent.atomic.AtomicReference;
public class EuiccController extends IEuiccController.Stub {
    private static final String TAG = "EuiccController";

    // Aliases so line lengths stay short.
    private static final int OK = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK;
    private static final int RESOLVABLE_ERROR =
            EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR;
    private static final int GENERIC_ERROR =
            EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR;

    private static EuiccController sInstance;

    private final Context mContext;
    private final EuiccConnector mConnector;
    private final AppOpsManager mAppOpsManager;
    private final PackageManager mPackageManager;

    /** Initialize the instance. Should only be called once. */
    public static EuiccController init(Context context) {
@@ -79,6 +91,8 @@ public class EuiccController extends IEuiccController.Stub {
    public EuiccController(Context context, EuiccConnector connector) {
        mContext = context;
        mConnector = connector;
        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        mPackageManager = context.getPackageManager();
    }

    /**
@@ -105,16 +119,26 @@ public class EuiccController extends IEuiccController.Stub {

    @Override
    public void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
            final PendingIntent callbackIntent) {
            PendingIntent callbackIntent) {
        if (!callerCanWriteEmbeddedSubscriptions()) {
            throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get metadata");
        }
        long token = Binder.clearCallingIdentity();
        try {
            final String subscriptionResultKey =
                    EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION;
            mConnector.getDownloadableSubscriptionMetadata(subscription,
                    new EuiccConnector.GetMetadataCommandCallback() {
            mConnector.getDownloadableSubscriptionMetadata(
                    subscription, new GetMetadataCommandCallback(callbackIntent));
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    class GetMetadataCommandCallback implements EuiccConnector.GetMetadataCommandCallback {
        protected final PendingIntent mCallbackIntent;

        GetMetadataCommandCallback(PendingIntent callbackIntent) {
            mCallbackIntent = callbackIntent;
        }

        @Override
        public void onGetMetadataComplete(
                GetDownloadableSubscriptionMetadataResult result) {
@@ -122,56 +146,130 @@ public class EuiccController extends IEuiccController.Stub {
            final int resultCode;
            switch (result.result) {
                case GetDownloadableSubscriptionMetadataResult.RESULT_OK:
                                    resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK;
                                    extrasIntent.putExtra(subscriptionResultKey,
                    resultCode = OK;
                    extrasIntent.putExtra(
                            EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION,
                            result.subscription);
                    break;
                                case GetDownloadableSubscriptionMetadataResult
                                        .RESULT_MUST_DEACTIVATE_REMOVABLE_SIM:
                                    resultCode = EuiccManager
                                            .EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR;
                case GetDownloadableSubscriptionMetadataResult.RESULT_MUST_DEACTIVATE_REMOVABLE_SIM:
                    resultCode = RESOLVABLE_ERROR;
                    // TODO(b/33075886): Pass through the PendingIntent for the
                    // resolution action.
                    break;
                case GetDownloadableSubscriptionMetadataResult.RESULT_GENERIC_ERROR:
                                    resultCode =
                                            EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR;
                    resultCode = GENERIC_ERROR;
                    extrasIntent.putExtra(
                            EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
                            result.detailedCode);
                    break;
                default:
                    Log.wtf(TAG, "Unknown result: " + result.result);
                                    resultCode =
                                            EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR;
                    resultCode = GENERIC_ERROR;
                    break;
            }

                            sendResult(callbackIntent, resultCode, extrasIntent);
            sendResult(mCallbackIntent, resultCode, extrasIntent);
        }

        @Override
        public void onEuiccServiceUnavailable() {
                            sendResult(callbackIntent,
                                    EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR,
                                    null /* extrasIntent */);
            sendResult(mCallbackIntent, GENERIC_ERROR, null /* extrasIntent */);
        }
                    });
    }

    @Override
    public void downloadSubscription(DownloadableSubscription subscription,
            boolean switchAfterDownload, String callingPackage, PendingIntent callbackIntent) {
        boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);

        long token = Binder.clearCallingIdentity();
        try {
            if (callerCanWriteEmbeddedSubscriptions) {
                // With WRITE_EMBEDDED_SUBSCRIPTIONS, we can skip profile-specific permission checks
                // and move straight to the profile download.
                downloadSubscriptionPrivileged(subscription, switchAfterDownload, callbackIntent);
                return;
            }
            // Without WRITE_EMBEDDED_SUBSCRIPTIONS, the caller *must* be whitelisted per the
            // metadata of the profile to be downloaded, so check the metadata first.
            mConnector.getDownloadableSubscriptionMetadata(subscription,
                    new DownloadSubscriptionGetMetadataCommandCallback(
                            switchAfterDownload, callbackIntent, callingPackage));
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    class DownloadSubscriptionGetMetadataCommandCallback extends GetMetadataCommandCallback {
        private final boolean mSwitchAfterDownload;
        private final String mCallingPackage;

        DownloadSubscriptionGetMetadataCommandCallback(
                boolean switchAfterDownload, PendingIntent callbackIntent, String callingPackage) {
            super(callbackIntent);
            mSwitchAfterDownload = switchAfterDownload;
            mCallingPackage = callingPackage;
        }

        @Override
    public void downloadSubscription(DownloadableSubscription subscription,
            boolean switchAfterDownload, final PendingIntent callbackIntent) {
        if (!callerCanWriteEmbeddedSubscriptions()) {
            // TODO(b/33075886): Allow unprivileged carriers who have carrier privileges on the
            // active mSubscription (if any) and the mSubscription to be downloaded.
            throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to download");
        public void onGetMetadataComplete(
                GetDownloadableSubscriptionMetadataResult result) {
            if (result.result != GetDownloadableSubscriptionMetadataResult.RESULT_OK) {
                // Just propagate the error as normal.
                // TODO(b/33075886): Pass through the PendingIntent for the resolution action. We
                // want to retry the parent download operation after resolution, not the get
                // metadata call.
                super.onGetMetadataComplete(result);
                return;
            }
        long token = Binder.clearCallingIdentity();

            DownloadableSubscription subscription = result.subscription;
            UiccAccessRule[] rules = subscription.getAccessRules();
            if (rules == null) {
                Log.e(TAG, "No access rules but caller is unprivileged");
                sendResult(mCallbackIntent, GENERIC_ERROR, null /* extrasIntent */);
                return;
            }

            final PackageInfo info;
            try {
                info = mPackageManager.getPackageInfo(
                        mCallingPackage, PackageManager.GET_SIGNATURES);
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "Calling package valid but gone");
                sendResult(mCallbackIntent, GENERIC_ERROR, null /* extrasIntent */);
                return;
            }

            for (int i = 0; i < rules.length; i++) {
                if (rules[i].getCarrierPrivilegeStatus(info)
                        == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
                    // TODO(b/33075886): For consistency, this should check the privilege rules in
                    // the metadata, not the profile itself.
                    TelephonyManager tm =
                            (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
                    if (tm.checkCarrierPrivilegesForPackage(mCallingPackage)
                            == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
                        // Permission verified - move on to the download.
                        downloadSubscriptionPrivileged(
                                subscription, mSwitchAfterDownload, mCallbackIntent);
                    } else {
                        // Switch might still be permitted, but the user must consent first.
                        // TODO(b/33075886): Pass through the PendingIntent for the resolution
                        // action.
                        sendResult(mCallbackIntent, RESOLVABLE_ERROR, null /* extrasIntent */);
                    }
                    return;
                }
            }
            Log.e(TAG, "Caller is not permitted to download this profile");
            sendResult(mCallbackIntent, GENERIC_ERROR, null /* extrasIntent */);
        }
    }

    private void downloadSubscriptionPrivileged(DownloadableSubscription subscription,
            boolean switchAfterDownload, final PendingIntent callbackIntent) {
        mConnector.downloadSubscription(subscription, switchAfterDownload,
                new EuiccConnector.DownloadCommandCallback() {
                    @Override
@@ -180,25 +278,22 @@ public class EuiccController extends IEuiccController.Stub {
                        final int resultCode;
                        switch (result.result) {
                            case DownloadResult.RESULT_OK:
                                    resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK;
                                resultCode = OK;
                                break;
                            case DownloadResult.RESULT_MUST_DEACTIVATE_REMOVABLE_SIM:
                                    resultCode = EuiccManager
                                            .EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR;
                                resultCode = RESOLVABLE_ERROR;
                                // TODO(b/33075886): Pass through the PendingIntent for the
                                // resolution action.
                                break;
                            case DownloadResult.RESULT_GENERIC_ERROR:
                                    resultCode =
                                            EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR;
                                resultCode = GENERIC_ERROR;
                                extrasIntent.putExtra(
                                        EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
                                        result.detailedCode);
                                break;
                            default:
                                Log.wtf(TAG, "Unknown result: " + result.result);
                                    resultCode =
                                            EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR;
                                resultCode = GENERIC_ERROR;
                                break;
                        }

@@ -207,14 +302,9 @@ public class EuiccController extends IEuiccController.Stub {

                    @Override
                    public void onEuiccServiceUnavailable() {
                            sendResult(callbackIntent,
                                    EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR,
                                    null /* extrasIntent */);
                        sendResult(callbackIntent, GENERIC_ERROR, null /* extrasIntent */);
                    }
                });
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private void sendResult(PendingIntent callbackIntent, int resultCode, Intent extrasIntent) {
+111 −13
Original line number Diff line number Diff line
@@ -18,9 +18,12 @@ package com.android.internal.telephony.euicc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -30,12 +33,16 @@ import android.app.PendingIntent;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Bundle;
import android.os.RemoteException;
import android.service.euicc.DownloadResult;
import android.service.euicc.GetDownloadableSubscriptionMetadataResult;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.TelephonyManager;
import android.telephony.UiccAccessRule;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccManager;

@@ -51,11 +58,33 @@ import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

@RunWith(AndroidJUnit4.class)
public class EuiccControllerTest extends TelephonyTest {
    private static final DownloadableSubscription SUBSCRIPTION =
            DownloadableSubscription.forActivationCode("abcde");

    private static final String PACKAGE_NAME = "test.package";
    private static final String CARRIER_NAME = "test name";
    private static final byte[] SIGNATURE_BYTES = new byte[] {1, 2, 3, 4, 5};

    private static final DownloadableSubscription SUBSCRIPTION_WITH_METADATA =
            DownloadableSubscription.forActivationCode("abcde");
    static {
        try {
            SUBSCRIPTION_WITH_METADATA.setCarrierName("test name");
            UiccAccessRule rule = new UiccAccessRule(
                    MessageDigest.getInstance("SHA-256").digest(SIGNATURE_BYTES),
                    PACKAGE_NAME,
                    0);
            SUBSCRIPTION_WITH_METADATA.setAccessRules(new UiccAccessRule[] { rule });
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256 must exist");
        }
    }

    @Mock private EuiccConnector mMockConnector;
    private EuiccController mController;

@@ -122,17 +151,15 @@ public class EuiccControllerTest extends TelephonyTest {
    @Test
    public void testGetDownloadableSubscriptionMetadata_success() throws Exception {
        setHasWriteEmbeddedPermission(true);
        DownloadableSubscription subscription = DownloadableSubscription.forActivationCode("abcde");
        subscription.setCarrierName("test name");
        GetDownloadableSubscriptionMetadataResult result =
                GetDownloadableSubscriptionMetadataResult.success(subscription);
                GetDownloadableSubscriptionMetadataResult.success(SUBSCRIPTION_WITH_METADATA);
        callGetDownloadableSubscriptionMetadata(SUBSCRIPTION, true /* complete */, result);
        Intent intent = verifyIntentSent(
                EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
        DownloadableSubscription receivedSubscription = intent.getParcelableExtra(
                EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION);
        assertNotNull(receivedSubscription);
        assertEquals("test name", receivedSubscription.getCarrierName());
        assertEquals(CARRIER_NAME, receivedSubscription.getCarrierName());
    }

    @Test
@@ -140,7 +167,7 @@ public class EuiccControllerTest extends TelephonyTest {
        setHasWriteEmbeddedPermission(true);
        callDownloadSubscription(
                SUBSCRIPTION, true /* switchAfterDownload */, false /* complete */,
                null /* result */);
                null /* result */, "whatever" /* callingPackage */);
        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR,
                0 /* detailedCode */);
    }
@@ -150,7 +177,7 @@ public class EuiccControllerTest extends TelephonyTest {
        setHasWriteEmbeddedPermission(true);
        DownloadResult result = DownloadResult.genericError(42);
        callDownloadSubscription(SUBSCRIPTION, false /* switchAfterDownload */, true /* complete */,
                result);
                result, "whatever" /* callingPackage */);
        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR,
                42 /* detailedCode */);
    }
@@ -160,12 +187,76 @@ public class EuiccControllerTest extends TelephonyTest {
        setHasWriteEmbeddedPermission(true);
        DownloadResult result = DownloadResult.success();
        callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
                result);
                result, "whatever" /* callingPackage */);
        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
    }

    // TODO(b/33075886): Add security tests once carrier privilege checks are implemented.
    // TODO(b/33075886): Add resolvable error tests once resolvable errors are implemented.
    @Test
    public void testDownloadSubscription_noPrivileges_getMetadata_serviceUnavailable()
            throws Exception {
        setHasWriteEmbeddedPermission(false);
        prepareGetDownloadableSubscriptionMetadataCall(false /* complete */, null /* result */);
        callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
                DownloadResult.success(), PACKAGE_NAME /* callingPackage */);
        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR,
                0 /* detailedCode */);
        verify(mMockConnector, never()).downloadSubscription(
                Mockito.any(), anyBoolean(), Mockito.any());
    }

    @Test
    public void testDownloadSubscription_noPrivileges_getMetadata_genericError()
            throws Exception {
        setHasWriteEmbeddedPermission(false);
        GetDownloadableSubscriptionMetadataResult result =
                GetDownloadableSubscriptionMetadataResult.genericError(42);
        prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
        callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
                DownloadResult.success(), PACKAGE_NAME /* callingPackage */);
        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR,
                42 /* detailedCode */);
        verify(mMockConnector, never()).downloadSubscription(
                Mockito.any(), anyBoolean(), Mockito.any());
    }

    @Test
    public void testDownloadSubscription_noPrivileges_hasCarrierPrivileges() throws Exception {
        setHasWriteEmbeddedPermission(false);
        GetDownloadableSubscriptionMetadataResult result =
                GetDownloadableSubscriptionMetadataResult.success(SUBSCRIPTION_WITH_METADATA);
        prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
        PackageInfo pi = new PackageInfo();
        pi.packageName = PACKAGE_NAME;
        pi.signatures = new Signature[] { new Signature(SIGNATURE_BYTES) };
        when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
        // TODO(b/33075886): This should mock the current profile metadata, not privileges.
        when(mTelephonyManager.checkCarrierPrivilegesForPackage(PACKAGE_NAME))
                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
        callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
                DownloadResult.success(), PACKAGE_NAME /* callingPackage */);
        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
    }

    @Test
    public void testDownloadSubscription_noPrivileges_noCarrierPrivileges() throws Exception {
        setHasWriteEmbeddedPermission(false);
        GetDownloadableSubscriptionMetadataResult result =
                GetDownloadableSubscriptionMetadataResult.success(SUBSCRIPTION_WITH_METADATA);
        prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
        PackageInfo pi = new PackageInfo();
        pi.packageName = PACKAGE_NAME;
        pi.signatures = new Signature[] { new Signature(new byte[] { 5, 4, 3, 2, 1 }) };
        when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
        callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
                DownloadResult.success(), PACKAGE_NAME /* callingPackage */);
        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR,
                0 /* detailedCode */);
        verify(mTelephonyManager, never()).checkCarrierPrivilegesForPackage(PACKAGE_NAME);
        verify(mMockConnector, never()).downloadSubscription(
                Mockito.any(), anyBoolean(), Mockito.any());
    }

    // TODO(b/33075886): Add resolvable error tests once resolvable errors are implemented

    private void setGetEidPermissions(
            boolean hasPhoneStatePrivileged, boolean hasCarrierPrivileges) {
@@ -199,9 +290,8 @@ public class EuiccControllerTest extends TelephonyTest {
        return mController.getEid();
    }

    private void callGetDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
    private void prepareGetDownloadableSubscriptionMetadataCall(
            final boolean complete, final GetDownloadableSubscriptionMetadataResult result) {
        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
        doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Exception {
@@ -216,11 +306,18 @@ public class EuiccControllerTest extends TelephonyTest {
        }).when(mMockConnector).getDownloadableSubscriptionMetadata(
                Mockito.<DownloadableSubscription>any(),
                Mockito.<EuiccConnector.GetMetadataCommandCallback>any());
    }

    private void callGetDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
            boolean complete, GetDownloadableSubscriptionMetadataResult result) {
        prepareGetDownloadableSubscriptionMetadataCall(complete, result);
        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
        mController.getDownloadableSubscriptionMetadata(subscription, resultCallback);
    }

    private void callDownloadSubscription(DownloadableSubscription subscription,
            boolean switchAfterDownload, final boolean complete, final DownloadResult result) {
            boolean switchAfterDownload, final boolean complete, final DownloadResult result,
            String callingPackage) {
        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
        doAnswer(new Answer<Void>() {
            @Override
@@ -237,7 +334,8 @@ public class EuiccControllerTest extends TelephonyTest {
                Mockito.<DownloadableSubscription>any(),
                Mockito.eq(switchAfterDownload),
                Mockito.<EuiccConnector.DownloadCommandCallback>any());
        mController.downloadSubscription(subscription, switchAfterDownload, resultCallback);
        mController.downloadSubscription(subscription, switchAfterDownload, callingPackage,
                resultCallback);
    }

    private Intent verifyIntentSent(int resultCode, int detailedCode)