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

Commit 8f32416b authored by Jeff Davidson's avatar Jeff Davidson
Browse files

Implement remaining core eUICC APIs.

These APIs follow the template of the APIs which have already been
implemented, in general. Switch, delete, and update nickname take a
subscription ID and map this to an ICCID before passing it to the
EuiccService implementation.

This completes the baseline implementation as designed; any additional
APIs or tweaks to the APIs will be tracked as separate bugs.

Fixes: 33075886
Test: Build/boot/unit test
Change-Id: I98ad64fb70b8989d14f3f3b6f875e693258faf5d
parent dce529f0
Loading
Loading
Loading
Loading
+228 −2
Original line number Diff line number Diff line
@@ -33,17 +33,29 @@ import android.content.pm.ServiceInfo;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.service.euicc.DeleteResult;
import android.service.euicc.DownloadResult;
import android.service.euicc.EraseResult;
import android.service.euicc.EuiccService;
import android.service.euicc.GetDefaultDownloadableSubscriptionListResult;
import android.service.euicc.GetDownloadableSubscriptionMetadataResult;
import android.service.euicc.GetEuiccProfileInfoListResult;
import android.service.euicc.IDeleteSubscriptionCallback;
import android.service.euicc.IDownloadSubscriptionCallback;
import android.service.euicc.IEraseSubscriptionsCallback;
import android.service.euicc.IEuiccService;
import android.service.euicc.IGetDefaultDownloadableSubscriptionListCallback;
import android.service.euicc.IGetDownloadableSubscriptionMetadataCallback;
import android.service.euicc.IGetEidCallback;
import android.service.euicc.IGetEuiccInfoCallback;
import android.service.euicc.IGetEuiccProfileInfoListCallback;
import android.service.euicc.ISwitchToSubscriptionCallback;
import android.service.euicc.IUpdateSubscriptionNicknameCallback;
import android.service.euicc.SwitchResult;
import android.service.euicc.UpdateNicknameResult;
import android.telephony.SubscriptionManager;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
import android.text.TextUtils;
import android.util.Log;

@@ -109,6 +121,12 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
    private static final int CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA = 7;
    private static final int CMD_DOWNLOAD_SUBSCRIPTION = 8;
    private static final int CMD_GET_EUICC_PROFILE_INFO_LIST = 9;
    private static final int CMD_GET_DEFAULT_DOWNLOADABLE_SUBSCRIPTION_LIST = 10;
    private static final int CMD_GET_EUICC_INFO = 11;
    private static final int CMD_DELETE_SUBSCRIPTION = 12;
    private static final int CMD_SWITCH_TO_SUBSCRIPTION = 13;
    private static final int CMD_UPDATE_SUBSCRIPTION_NICKNAME = 14;
    private static final int CMD_ERASE_SUBSCRIPTIONS = 15;

    private static boolean isEuiccCommand(int what) {
        return what >= CMD_GET_EID;
@@ -180,6 +198,70 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
        void onListComplete(GetEuiccProfileInfoListResult result);
    }

    static class GetDefaultListRequest {
        boolean mForceDeactivateSim;
        GetDefaultListCommandCallback mCallback;
    }

    /** Callback class for {@link #getDefaultDownloadableSubscriptionList}. */
    @VisibleForTesting(visibility = PACKAGE)
    public interface GetDefaultListCommandCallback extends BaseEuiccCommandCallback {
        /** Called when the list has completed (though it may have failed). */
        void onGetDefaultListComplete(GetDefaultDownloadableSubscriptionListResult result);
    }

    /** Callback class for {@link #getEuiccInfo}. */
    @VisibleForTesting(visibility = PACKAGE)
    public interface GetEuiccInfoCommandCallback extends BaseEuiccCommandCallback {
        /** Called when the EuiccInfo lookup has completed. */
        void onGetEuiccInfoComplete(EuiccInfo euiccInfo);
    }

    static class DeleteRequest {
        String mIccid;
        DeleteCommandCallback mCallback;
    }

    /** Callback class for {@link #deleteSubscription}. */
    @VisibleForTesting(visibility = PACKAGE)
    public interface DeleteCommandCallback extends BaseEuiccCommandCallback {
        /** Called when the delete has completed (though it may have failed). */
        void onDeleteComplete(DeleteResult result);
    }

    static class SwitchRequest {
        @Nullable String mIccid;
        boolean mForceDeactivateSim;
        SwitchCommandCallback mCallback;
    }

    /** Callback class for {@link #switchToSubscription}. */
    @VisibleForTesting(visibility = PACKAGE)
    public interface SwitchCommandCallback extends BaseEuiccCommandCallback {
        /** Called when the switch has completed (though it may have failed). */
        void onSwitchComplete(SwitchResult result);
    }

    static class UpdateNicknameRequest {
        String mIccid;
        String mNickname;
        UpdateNicknameCommandCallback mCallback;
    }

    /** Callback class for {@link #updateSubscriptionNickname}. */
    @VisibleForTesting(visibility = PACKAGE)
    public interface UpdateNicknameCommandCallback extends BaseEuiccCommandCallback {
        /** Called when the update has completed (though it may have failed). */
        void onUpdateNicknameComplete(UpdateNicknameResult result);
    }

    /** Callback class for {@link #eraseSubscriptions}. */
    @VisibleForTesting(visibility = PACKAGE)
    public interface EraseCommandCallback extends BaseEuiccCommandCallback {
        /** Called when the erase has completed (though it may have failed). */
        void onEraseComplete(EraseResult result);
    }

    private Context mContext;
    private PackageManager mPm;

@@ -265,7 +347,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
        sendMessage(CMD_GET_EID, callback);
    }

    /** Asynchronously fetch metadata for the given downloadable mSubscription. */
    /** Asynchronously fetch metadata for the given downloadable subscription. */
    @VisibleForTesting(visibility = PACKAGE)
    public void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
            boolean forceDeactivateSim, GetMetadataCommandCallback callback) {
@@ -277,7 +359,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
        sendMessage(CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA, request);
    }

    /** Asynchronously download the given mSubscription. */
    /** Asynchronously download the given subscription. */
    @VisibleForTesting(visibility = PACKAGE)
    public void downloadSubscription(DownloadableSubscription subscription,
            boolean switchAfterDownload, boolean forceDeactivateSim,
@@ -294,6 +376,59 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
        sendMessage(CMD_GET_EUICC_PROFILE_INFO_LIST, callback);
    }

    /** Asynchronously fetch the default downloadable subscription list. */
    @VisibleForTesting(visibility = PACKAGE)
    public void getDefaultDownloadableSubscriptionList(
            boolean forceDeactivateSim, GetDefaultListCommandCallback callback) {
        GetDefaultListRequest request = new GetDefaultListRequest();
        request.mForceDeactivateSim = forceDeactivateSim;
        request.mCallback = callback;
        sendMessage(CMD_GET_DEFAULT_DOWNLOADABLE_SUBSCRIPTION_LIST, request);
    }

    /** Asynchronously fetch the {@link EuiccInfo}. */
    @VisibleForTesting(visibility = PACKAGE)
    public void getEuiccInfo(GetEuiccInfoCommandCallback callback) {
        sendMessage(CMD_GET_EUICC_INFO, callback);
    }

    /** Asynchronously delete the given subscription. */
    @VisibleForTesting(visibility = PACKAGE)
    public void deleteSubscription(String iccid, DeleteCommandCallback callback) {
        DeleteRequest request = new DeleteRequest();
        request.mIccid = iccid;
        request.mCallback = callback;
        sendMessage(CMD_DELETE_SUBSCRIPTION, request);
    }

    /** Asynchronously switch to the given subscription. */
    @VisibleForTesting(visibility = PACKAGE)
    public void switchToSubscription(@Nullable String iccid, boolean forceDeactivateSim,
            SwitchCommandCallback callback) {
        SwitchRequest request = new SwitchRequest();
        request.mIccid = iccid;
        request.mForceDeactivateSim = forceDeactivateSim;
        request.mCallback = callback;
        sendMessage(CMD_SWITCH_TO_SUBSCRIPTION, request);
    }

    /** Asynchronously update the nickname of the given subscription. */
    @VisibleForTesting(visibility = PACKAGE)
    public void updateSubscriptionNickname(
            String iccid, String nickname, UpdateNicknameCommandCallback callback) {
        UpdateNicknameRequest request = new UpdateNicknameRequest();
        request.mIccid = iccid;
        request.mNickname = nickname;
        request.mCallback = callback;
        sendMessage(CMD_UPDATE_SUBSCRIPTION_NICKNAME, request);
    }

    /** Asynchronously erase all profiles on the eUICC. */
    @VisibleForTesting(visibility = PACKAGE)
    public void eraseSubscriptions(EraseCommandCallback callback) {
        sendMessage(CMD_ERASE_SUBSCRIPTIONS, callback);
    }

    /**
     * State in which no EuiccService is available.
     *
@@ -522,6 +657,87 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
                                    });
                            break;
                        }
                        case CMD_GET_DEFAULT_DOWNLOADABLE_SUBSCRIPTION_LIST: {
                            GetDefaultListRequest request = (GetDefaultListRequest) message.obj;
                            mEuiccService.getDefaultDownloadableSubscriptionList(slotId,
                                    request.mForceDeactivateSim,
                                    new IGetDefaultDownloadableSubscriptionListCallback.Stub() {
                                        @Override
                                        public void onComplete(
                                                GetDefaultDownloadableSubscriptionListResult result
                                        ) {
                                            ((GetDefaultListCommandCallback) callback)
                                                    .onGetDefaultListComplete(result);
                                            onCommandEnd();
                                        }
                                    });
                            break;
                        }
                        case CMD_GET_EUICC_INFO: {
                            mEuiccService.getEuiccInfo(slotId,
                                    new IGetEuiccInfoCallback.Stub() {
                                        @Override
                                        public void onSuccess(EuiccInfo euiccInfo) {
                                            ((GetEuiccInfoCommandCallback) callback)
                                                    .onGetEuiccInfoComplete(euiccInfo);
                                            onCommandEnd();
                                        }
                                    });
                            break;
                        }
                        case CMD_DELETE_SUBSCRIPTION: {
                            DeleteRequest request = (DeleteRequest) message.obj;
                            mEuiccService.deleteSubscription(slotId, request.mIccid,
                                    new IDeleteSubscriptionCallback.Stub() {
                                        @Override
                                        public void onComplete(DeleteResult result) {
                                            ((DeleteCommandCallback) callback)
                                                    .onDeleteComplete(result);
                                            onCommandEnd();
                                        }
                                    });
                            break;
                        }
                        case CMD_SWITCH_TO_SUBSCRIPTION: {
                            SwitchRequest request = (SwitchRequest) message.obj;
                            mEuiccService.switchToSubscription(slotId, request.mIccid,
                                    request.mForceDeactivateSim,
                                    new ISwitchToSubscriptionCallback.Stub() {
                                        @Override
                                        public void onComplete(SwitchResult result) {
                                            ((SwitchCommandCallback) callback)
                                                    .onSwitchComplete(result);
                                            onCommandEnd();
                                        }
                                    });
                            break;
                        }
                        case CMD_UPDATE_SUBSCRIPTION_NICKNAME: {
                            UpdateNicknameRequest request = (UpdateNicknameRequest) message.obj;
                            mEuiccService.updateSubscriptionNickname(slotId, request.mIccid,
                                    request.mNickname,
                                    new IUpdateSubscriptionNicknameCallback.Stub() {
                                        @Override
                                        public void onComplete(UpdateNicknameResult result) {
                                            ((UpdateNicknameCommandCallback) callback)
                                                    .onUpdateNicknameComplete(result);
                                            onCommandEnd();
                                        }
                                    });
                            break;
                        }
                        case CMD_ERASE_SUBSCRIPTIONS: {
                            mEuiccService.eraseSubscriptions(slotId,
                                    new IEraseSubscriptionsCallback.Stub() {
                                        @Override
                                        public void onComplete(EraseResult result) {
                                            ((EraseCommandCallback) callback)
                                                    .onEraseComplete(result);
                                            onCommandEnd();
                                        }
                                    });
                            break;
                        }
                        default: {
                            Log.wtf(TAG, "Unimplemented eUICC command: " + message.what);
                            callback.onEuiccServiceUnavailable();
@@ -554,11 +770,21 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
        switch (message.what) {
            case CMD_GET_EID:
            case CMD_GET_EUICC_PROFILE_INFO_LIST:
            case CMD_GET_EUICC_INFO:
            case CMD_ERASE_SUBSCRIPTIONS:
                return (BaseEuiccCommandCallback) message.obj;
            case CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA:
                return ((GetMetadataRequest) message.obj).mCallback;
            case CMD_DOWNLOAD_SUBSCRIPTION:
                return ((DownloadRequest) message.obj).mCallback;
            case CMD_GET_DEFAULT_DOWNLOADABLE_SUBSCRIPTION_LIST:
                return ((GetDefaultListRequest) message.obj).mCallback;
            case CMD_DELETE_SUBSCRIPTION:
                return ((DeleteRequest) message.obj).mCallback;
            case CMD_SWITCH_TO_SUBSCRIPTION:
                return ((SwitchRequest) message.obj).mCallback;
            case CMD_UPDATE_SUBSCRIPTION_NICKNAME:
                return ((UpdateNicknameRequest) message.obj).mCallback;
            default:
                throw new IllegalArgumentException("Unsupported message: " + message.what);
        }
+422 −20

File changed.

Preview size limit exceeded, changes collapsed.

+108 −4
Original line number Diff line number Diff line
@@ -70,6 +70,12 @@ public class EuiccOperation implements Parcelable {
    static final int ACTION_DOWNLOAD_DEACTIVATE_SIM = 2;
    @VisibleForTesting
    static final int ACTION_DOWNLOAD_NO_PRIVILEGES = 3;
    @VisibleForTesting
    static final int ACTION_GET_DEFAULT_LIST_DEACTIVATE_SIM = 4;
    @VisibleForTesting
    static final int ACTION_SWITCH_DEACTIVATE_SIM = 5;
    @VisibleForTesting
    static final int ACTION_SWITCH_NO_PRIVILEGES = 6;

    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public final @Action int mAction;
@@ -78,7 +84,9 @@ public class EuiccOperation implements Parcelable {

    @Nullable
    private final DownloadableSubscription mDownloadableSubscription;
    private final int mSubscriptionId;
    private final boolean mSwitchAfterDownload;
    @Nullable
    private final String mCallingPackage;

    /**
@@ -88,7 +96,8 @@ public class EuiccOperation implements Parcelable {
    public static EuiccOperation forGetMetadataDeactivateSim(long callingToken,
            DownloadableSubscription subscription) {
        return new EuiccOperation(ACTION_GET_METADATA_DEACTIVATE_SIM, callingToken,
                subscription, false /* switchAfterDownload */, null /* callingPackage */);
                subscription, 0 /* subscriptionId */, false /* switchAfterDownload */,
                null /* callingPackage */);
    }

    /**
@@ -99,7 +108,7 @@ public class EuiccOperation implements Parcelable {
            DownloadableSubscription subscription, boolean switchAfterDownload,
            String callingPackage) {
        return new EuiccOperation(ACTION_DOWNLOAD_DEACTIVATE_SIM, callingToken,
                subscription, switchAfterDownload, callingPackage);
                subscription,  0 /* subscriptionId */, switchAfterDownload, callingPackage);
    }

    /**
@@ -107,19 +116,42 @@ public class EuiccOperation implements Parcelable {
     * permission to manage the current active subscription.
     */
    public static EuiccOperation forDownloadNoPrivileges(long callingToken,
            DownloadableSubscription subscription, boolean switchAfterDownload) {
            DownloadableSubscription subscription, boolean switchAfterDownload,
            String callingPackage) {
        return new EuiccOperation(ACTION_DOWNLOAD_NO_PRIVILEGES, callingToken,
                subscription, switchAfterDownload, null /* callingPackage */);
                subscription,  0 /* subscriptionId */, switchAfterDownload, callingPackage);
    }

    static EuiccOperation forGetDefaultListDeactivateSim(long callingToken) {
        return new EuiccOperation(ACTION_GET_DEFAULT_LIST_DEACTIVATE_SIM, callingToken,
                null /* downloadableSubscription */, 0 /* subscriptionId */,
                false /* switchAfterDownload */, null /* callingPackage */);
    }

    static EuiccOperation forSwitchDeactivateSim(long callingToken, int subscriptionId,
            String callingPackage) {
        return new EuiccOperation(ACTION_SWITCH_DEACTIVATE_SIM, callingToken,
                null /* downloadableSubscription */, subscriptionId,
                false /* switchAfterDownload */, callingPackage);
    }

    static EuiccOperation forSwitchNoPrivileges(long callingToken, int subscriptionId,
            String callingPackage) {
        return new EuiccOperation(ACTION_SWITCH_NO_PRIVILEGES, callingToken,
                null /* downloadableSubscription */, subscriptionId,
                false /* switchAfterDownload */, callingPackage);
    }

    EuiccOperation(@Action int action,
            long callingToken,
            @Nullable DownloadableSubscription downloadableSubscription,
            int subscriptionId,
            boolean switchAfterDownload,
            String callingPackage) {
        mAction = action;
        mCallingToken = callingToken;
        mDownloadableSubscription = downloadableSubscription;
        mSubscriptionId = subscriptionId;
        mSwitchAfterDownload = switchAfterDownload;
        mCallingPackage = callingPackage;
    }
@@ -128,6 +160,7 @@ public class EuiccOperation implements Parcelable {
        mAction = in.readInt();
        mCallingToken = in.readLong();
        mDownloadableSubscription = in.readTypedObject(DownloadableSubscription.CREATOR);
        mSubscriptionId = in.readInt();
        mSwitchAfterDownload = in.readBoolean();
        mCallingPackage = in.readString();
    }
@@ -137,6 +170,7 @@ public class EuiccOperation implements Parcelable {
        dest.writeInt(mAction);
        dest.writeLong(mCallingToken);
        dest.writeTypedObject(mDownloadableSubscription, flags);
        dest.writeInt(mSubscriptionId);
        dest.writeBoolean(mSwitchAfterDownload);
        dest.writeString(mCallingPackage);
    }
@@ -170,6 +204,21 @@ public class EuiccOperation implements Parcelable {
                        resolutionExtras.getBoolean(EuiccService.RESOLUTION_EXTRA_CONSENT),
                        callbackIntent);
                break;
            case ACTION_GET_DEFAULT_LIST_DEACTIVATE_SIM:
                resolvedGetDefaultListDeactivateSim(
                        resolutionExtras.getBoolean(EuiccService.RESOLUTION_EXTRA_CONSENT),
                        callbackIntent);
                break;
            case ACTION_SWITCH_DEACTIVATE_SIM:
                resolvedSwitchDeactivateSim(
                        resolutionExtras.getBoolean(EuiccService.RESOLUTION_EXTRA_CONSENT),
                        callbackIntent);
                break;
            case ACTION_SWITCH_NO_PRIVILEGES:
                resolvedSwitchNoPrivileges(
                        resolutionExtras.getBoolean(EuiccService.RESOLUTION_EXTRA_CONSENT),
                        callbackIntent);
                break;
            default:
                Log.wtf(TAG, "Unknown action: " + mAction);
                break;
@@ -235,6 +284,61 @@ public class EuiccOperation implements Parcelable {
        }
    }

    private void resolvedGetDefaultListDeactivateSim(
            boolean consent, PendingIntent callbackIntent) {
        if (consent) {
            // User has consented; perform the lookup, but this time, tell the LPA to deactivate any
            // required active SIMs.
            EuiccController.get().getDefaultDownloadableSubscriptionList(
                    true /* forceDeactivateSim */, callbackIntent);
        } else {
            // User has not consented; fail the operation.
            fail(callbackIntent);
        }
    }

    private void resolvedSwitchDeactivateSim(
            boolean consent, PendingIntent callbackIntent) {
        if (consent) {
            // User has consented; perform the switch, but this time, tell the LPA to deactivate any
            // required active SIMs.
            EuiccController.get().switchToSubscription(
                    mSubscriptionId,
                    true /* forceDeactivateSim */,
                    mCallingPackage,
                    callbackIntent);
        } else {
            // User has not consented; fail the operation.
            fail(callbackIntent);
        }
    }

    private void resolvedSwitchNoPrivileges(boolean consent, PendingIntent callbackIntent) {
        if (consent) {
            // User has consented; perform the switch with full privileges.
            long token = Binder.clearCallingIdentity();
            try {
                // Note: We turn on "forceDeactivateSim" here under the assumption that the
                // privilege prompt should also cover permission to deactivate an active SIM, as
                // the privilege prompt makes it clear that we're switching from the current
                // carrier. Also note that in practice, we'd need to deactivate the active SIM to
                // even reach this point, because we cannot fetch the metadata needed to check the
                // privileges without doing so.
                EuiccController.get().switchToSubscriptionPrivileged(
                        token,
                        mSubscriptionId,
                        true /* forceDeactivateSim */,
                        mCallingPackage,
                        callbackIntent);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        } else {
            // User has not consented; fail the operation.
            fail(callbackIntent);
        }
    }

    private static void fail(PendingIntent callbackIntent) {
        EuiccController.get().sendResult(
                callbackIntent,
+476 −18

File changed.

Preview size limit exceeded, changes collapsed.