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

Commit a9c50823 authored by Jeff Davidson's avatar Jeff Davidson
Browse files

Passthrough implementation of retainSubscriptionsForFactoryReset.

Change-Id: I000aed82e63a22d5d29667a277171e43c4aec25c
Test: TreeHugger
Fixes: 62681577
parent 2d56a524
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import android.service.euicc.IGetDownloadableSubscriptionMetadataCallback;
import android.service.euicc.IGetEidCallback;
import android.service.euicc.IGetEuiccInfoCallback;
import android.service.euicc.IGetEuiccProfileInfoListCallback;
import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback;
import android.service.euicc.ISwitchToSubscriptionCallback;
import android.service.euicc.IUpdateSubscriptionNicknameCallback;
import android.telephony.SubscriptionManager;
@@ -130,6 +131,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
    private static final int CMD_SWITCH_TO_SUBSCRIPTION = 107;
    private static final int CMD_UPDATE_SUBSCRIPTION_NICKNAME = 108;
    private static final int CMD_ERASE_SUBSCRIPTIONS = 109;
    private static final int CMD_RETAIN_SUBSCRIPTIONS = 110;

    private static boolean isEuiccCommand(int what) {
        return what >= CMD_GET_EID;
@@ -265,6 +267,13 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
        void onEraseComplete(int result);
    }

    /** Callback class for {@link #retainSubscriptions}. */
    @VisibleForTesting(visibility = PACKAGE)
    public interface RetainSubscriptionsCommandCallback extends BaseEuiccCommandCallback {
        /** Called when the retain command has completed (though it may have failed). */
        void onRetainSubscriptionsComplete(int result);
    }

    private Context mContext;
    private PackageManager mPm;

@@ -432,6 +441,12 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
        sendMessage(CMD_ERASE_SUBSCRIPTIONS, callback);
    }

    /** Asynchronously ensure that all profiles will be retained on the next factory reset. */
    @VisibleForTesting(visibility = PACKAGE)
    public void retainSubscriptions(RetainSubscriptionsCommandCallback callback) {
        sendMessage(CMD_RETAIN_SUBSCRIPTIONS, callback);
    }

    /**
     * State in which no EuiccService is available.
     *
@@ -766,6 +781,20 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
                                    });
                            break;
                        }
                        case CMD_RETAIN_SUBSCRIPTIONS: {
                            mEuiccService.retainSubscriptionsForFactoryReset(slotId,
                                    new IRetainSubscriptionsForFactoryResetCallback.Stub() {
                                        @Override
                                        public void onComplete(int result) {
                                            sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
                                                ((RetainSubscriptionsCommandCallback) callback)
                                                        .onRetainSubscriptionsComplete(result);
                                                onCommandEnd(callback);
                                            });
                                        }
                                    });
                            break;
                        }
                        default: {
                            Log.wtf(TAG, "Unimplemented eUICC command: " + message.what);
                            callback.onEuiccServiceUnavailable();
@@ -807,6 +836,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
            case CMD_GET_EUICC_PROFILE_INFO_LIST:
            case CMD_GET_EUICC_INFO:
            case CMD_ERASE_SUBSCRIPTIONS:
            case CMD_RETAIN_SUBSCRIPTIONS:
                return (BaseEuiccCommandCallback) message.obj;
            case CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA:
                return ((GetMetadataRequest) message.obj).mCallback;
+37 −0
Original line number Diff line number Diff line
@@ -808,6 +808,43 @@ public class EuiccController extends IEuiccController.Stub {
        }
    }

    @Override
    public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
        mContext.enforceCallingPermission(Manifest.permission.MASTER_CLEAR,
                "Must have MASTER_CLEAR to retain subscriptions for factory reset");
        long token = Binder.clearCallingIdentity();
        try {
            mConnector.retainSubscriptions(
                    new EuiccConnector.RetainSubscriptionsCommandCallback() {
                        @Override
                        public void onRetainSubscriptionsComplete(int result) {
                            Intent extrasIntent = new Intent();
                            final int resultCode;
                            switch (result) {
                                case EuiccService.RESULT_OK:
                                    resultCode = OK;
                                    break;
                                default:
                                    resultCode = ERROR;
                                    extrasIntent.putExtra(
                                            EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
                                            result);
                                    break;
                            }

                            sendResult(callbackIntent, resultCode, extrasIntent);
                        }

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

    /** Refresh the embedded subscription list and dispatch the given result upon completion. */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    public void refreshSubscriptionsAndSendResult(
+54 −0
Original line number Diff line number Diff line
@@ -26,7 +26,9 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -63,6 +65,7 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.stubbing.Stubber;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -722,6 +725,34 @@ public class EuiccControllerTest extends TelephonyTest {
        assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult);
    }

    @Test(expected = SecurityException.class)
    public void testRetainSubscriptionsForFactoryReset_noPrivileges() throws Exception {
        setHasMasterClearPermission(false);
        callRetainSubscriptionsForFactoryReset(false /* complete */, 0 /* result */);
    }

    @Test
    public void testRetainSubscriptionsForFactoryReset_serviceUnavailable() throws Exception {
        setHasMasterClearPermission(true);
        callRetainSubscriptionsForFactoryReset(false /* complete */, 0 /* result */);
        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 0 /* detailedCode */);
        verify(mMockConnector).retainSubscriptions(any());
    }

    @Test
    public void testRetainSubscriptionsForFactoryReset_error() throws Exception {
        setHasMasterClearPermission(true);
        callRetainSubscriptionsForFactoryReset(true /* complete */, 42 /* result */);
        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 42 /* detailedCode */);
    }

    @Test
    public void testRetainSubscriptionsForFactoryReset_success() throws Exception {
        setHasMasterClearPermission(true);
        callRetainSubscriptionsForFactoryReset(true /* complete */, EuiccService.RESULT_OK);
        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
    }

    private void setGetEidPermissions(
            boolean hasPhoneStatePrivileged, boolean hasCarrierPrivileges) {
        doReturn(hasPhoneStatePrivileged
@@ -738,6 +769,12 @@ public class EuiccControllerTest extends TelephonyTest {
                .checkCallingPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
    }

    private void setHasMasterClearPermission(boolean hasPermission) {
        Stubber stubber = hasPermission ? doNothing() : doThrow(new SecurityException());
        stubber.when(mContext).enforceCallingPermission(
                eq(Manifest.permission.MASTER_CLEAR), anyString());
    }

    private void setHasCarrierPrivilegesOnActiveSubscription(boolean hasPrivileges)
            throws Exception {
        SubscriptionInfo subInfo = new SubscriptionInfo(
@@ -924,6 +961,23 @@ public class EuiccControllerTest extends TelephonyTest {
        mController.eraseSubscriptions(resultCallback);
    }

    private void callRetainSubscriptionsForFactoryReset(final boolean complete, final int result) {
        PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
        doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Exception {
                EuiccConnector.RetainSubscriptionsCommandCallback cb = invocation.getArgument(0);
                if (complete) {
                    cb.onRetainSubscriptionsComplete(result);
                } else {
                    cb.onEuiccServiceUnavailable();
                }
                return null;
            }
        }).when(mMockConnector).retainSubscriptions(any());
        mController.retainSubscriptionsForFactoryReset(resultCallback);
    }

    private void verifyResolutionIntent(String euiccUiAction, @EuiccOperation.Action int action) {
        assertEquals(euiccUiAction, mController.mResolutionAction);
        assertNotNull(mController.mOp);