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

Commit 0a2c99b1 authored by Qingxi Li's avatar Qingxi Li
Browse files

Add API EuiccController#startOtaUpdatingIfNecessary

This function requests calling app has WRITE_EMBEDDED_SUBSCRIPTIONS
permission. It will check whether the OTA update needed to be done
first. If current eUICC OS isn't latest one and OTA needed to be
performed, it will update eUICC OS. When the OS update is started or
finished, a broadcast will be sent.

Bug: 37279356
Test: Included
Merged-In: I68f88dd01f23259859f6069046a96fa23b880c24
Change-Id: I68f88dd01f23259859f6069046a96fa23b880c24
parent 4c5c4510
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.content.pm.ServiceInfo;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.service.euicc.EuiccService;
import android.service.euicc.GetDefaultDownloadableSubscriptionListResult;
import android.service.euicc.GetDownloadableSubscriptionMetadataResult;
@@ -47,12 +48,14 @@ import android.service.euicc.IGetEidCallback;
import android.service.euicc.IGetEuiccInfoCallback;
import android.service.euicc.IGetEuiccProfileInfoListCallback;
import android.service.euicc.IGetOtaStatusCallback;
import android.service.euicc.IOtaStatusChangedCallback;
import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback;
import android.service.euicc.ISwitchToSubscriptionCallback;
import android.service.euicc.IUpdateSubscriptionNicknameCallback;
import android.telephony.SubscriptionManager;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
import android.telephony.euicc.EuiccManager;
import android.telephony.euicc.EuiccManager.OtaStatus;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -135,6 +138,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
    private static final int CMD_ERASE_SUBSCRIPTIONS = 109;
    private static final int CMD_RETAIN_SUBSCRIPTIONS = 110;
    private static final int CMD_GET_OTA_STATUS = 111;
    private static final int CMD_START_OTA_IF_NECESSARY = 112;

    private static boolean isEuiccCommand(int what) {
        return what >= CMD_GET_EID;
@@ -195,6 +199,14 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
        void onGetOtaStatusComplete(@OtaStatus int status);
    }

    /** Callback class for {@link #startOtaIfNecessary}. */
    @VisibleForTesting(visibility = PACKAGE)
    public interface OtaStatusChangedCallback extends BaseEuiccCommandCallback {
        /**
         * Called when OTA status is changed to {@link EuiccM}. */
        void onOtaStatusChanged(int status);
    }

    static class GetMetadataRequest {
        DownloadableSubscription mSubscription;
        boolean mForceDeactivateSim;
@@ -389,6 +401,12 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
        sendMessage(CMD_GET_OTA_STATUS, callback);
    }

    /** Asynchronously perform OTA update. */
    @VisibleForTesting(visibility = PACKAGE)
    public void startOtaIfNecessary(OtaStatusChangedCallback callback) {
        sendMessage(CMD_START_OTA_IF_NECESSARY, callback);
    }

    /** Asynchronously fetch metadata for the given downloadable subscription. */
    @VisibleForTesting(visibility = PACKAGE)
    public void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
@@ -839,6 +857,28 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
                                    });
                            break;
                        }
                        case CMD_START_OTA_IF_NECESSARY: {
                            mEuiccService.startOtaIfNecessary(slotId,
                                    new IOtaStatusChangedCallback.Stub() {
                                        @Override
                                        public void onOtaStatusChanged(int status)
                                                throws RemoteException {
                                            if (status == EuiccManager.EUICC_OTA_IN_PROGRESS) {
                                                sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
                                                    ((OtaStatusChangedCallback) callback)
                                                            .onOtaStatusChanged(status);
                                                });
                                            } else {
                                                sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
                                                    ((OtaStatusChangedCallback) callback)
                                                            .onOtaStatusChanged(status);
                                                    onCommandEnd(callback);
                                                });
                                            }
                                        }
                                    });
                            break;
                        }
                        default: {
                            Log.wtf(TAG, "Unimplemented eUICC command: " + message.what);
                            callback.onEuiccServiceUnavailable();
@@ -882,6 +922,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
            case CMD_ERASE_SUBSCRIPTIONS:
            case CMD_RETAIN_SUBSCRIPTIONS:
            case CMD_GET_OTA_STATUS:
            case CMD_START_OTA_IF_NECESSARY:
                return (BaseEuiccCommandCallback) message.obj;
            case CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA:
                return ((GetMetadataRequest) message.obj).mCallback;
+32 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.internal.telephony.euicc;
import static android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE;

import android.Manifest;
import android.Manifest.permission;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.PendingIntent;
@@ -46,6 +47,7 @@ import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -191,6 +193,26 @@ public class EuiccController extends IEuiccController.Stub {
        }
    }


    /**
     * Start eUICC OTA update if current eUICC OS is not the latest one. When OTA is started or
     * finished, the broadcast {@link EuiccManager#ACTION_OTA_STATUS_CHANGED} will be sent.
     *
     * This function will only be called from phone process and isn't exposed to the other apps.
     */
    public void startOtaUpdatingIfNecessary() {
        mConnector.startOtaIfNecessary(
                new OtaStatusChangedCallback() {
                    @Override
                    public void onOtaStatusChanged(int status) {
                        sendOtaStatusChangedBroadcast();
                    }

                    @Override
                    public void onEuiccServiceUnavailable() {}
                });
    }

    @Override
    public void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
            String callingPackage, PendingIntent callbackIntent) {
@@ -943,6 +965,16 @@ public class EuiccController extends IEuiccController.Stub {
        }
    }

    /**
     * Send broadcast {@link EuiccManager#ACTION_OTA_STATUS_CHANGED} for OTA status
     * changed.
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    public void sendOtaStatusChangedBroadcast() {
        Intent intent = new Intent(EuiccManager.ACTION_OTA_STATUS_CHANGED);
        mContext.sendBroadcast(intent, permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
    }

    @Nullable
    private SubscriptionInfo getSubscriptionForSubscriptionId(int subscriptionId) {
        List<SubscriptionInfo> subs = mSubscriptionManager.getAvailableSubscriptionInfoList();
+54 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import android.telephony.euicc.EuiccManager;

import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.euicc.EuiccConnector.GetOtaStatusCommandCallback;
import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback;

import org.junit.After;
import org.junit.Before;
@@ -125,8 +126,12 @@ public class EuiccControllerTest extends TelephonyTest {
        // Whether refreshSubscriptionsAndSendResult was called.
        private boolean mCalledRefreshSubscriptionsAndSendResult;

        // Number of OTA status changed.
        private int mNumOtaStatusChanged;

        TestEuiccController(Context context, EuiccConnector connector) {
            super(context, connector);
            mNumOtaStatusChanged = 0;
        }

        @Override
@@ -151,6 +156,11 @@ public class EuiccControllerTest extends TelephonyTest {
            mCalledRefreshSubscriptionsAndSendResult = true;
            sendResult(callbackIntent, resultCode, extrasIntent);
        }

        @Override
        public void sendOtaStatusChangedBroadcast() {
            ++mNumOtaStatusChanged;
        }
    }

    @Before
@@ -226,6 +236,32 @@ public class EuiccControllerTest extends TelephonyTest {
                callGetOtaStatus(false /* success */, 1 /* status */));
    }

    @Test
    public void testStartOtaUpdatingIfNecessary_serviceNotAvailable() {
        setHasWriteEmbeddedPermission(true /* hasPermission */);
        callStartOtaUpdatingIfNecessary(
                false /* serviceAvailable */, EuiccManager.EUICC_OTA_IN_PROGRESS);
        assertEquals(mController.mNumOtaStatusChanged, 0);
    }

    @Test
    public void testStartOtaUpdatingIfNecessary_otaStatusChanged() {
        setHasWriteEmbeddedPermission(true /* hasPermission */);
        callStartOtaUpdatingIfNecessary(
                true /* serviceAvailable */, EuiccManager.EUICC_OTA_IN_PROGRESS);
        callStartOtaUpdatingIfNecessary(
                true /* serviceAvailable */, EuiccManager.EUICC_OTA_FAILED);
        callStartOtaUpdatingIfNecessary(
                true /* serviceAvailable */, EuiccManager.EUICC_OTA_SUCCEEDED);
        callStartOtaUpdatingIfNecessary(
                true /* serviceAvailable */, EuiccManager.EUICC_OTA_NOT_NEEDED);
        callStartOtaUpdatingIfNecessary(
                true /* serviceAvailable */, EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE);

        assertEquals(mController.mNumOtaStatusChanged, 5);
    }


    @Test
    public void testGetEuiccInfo_success() {
        assertEquals(OS_VERSION, callGetEuiccInfo(true /* success */, EUICC_INFO).osVersion);
@@ -845,6 +881,24 @@ public class EuiccControllerTest extends TelephonyTest {
        return mController.getOtaStatus();
    }

    private void callStartOtaUpdatingIfNecessary(
            final boolean serviceAvailable, int status) {
        doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Exception {
                OtaStatusChangedCallback cb = invocation.getArgument(0);
                if (!serviceAvailable) {
                    cb.onEuiccServiceUnavailable();
                } else {
                    cb.onOtaStatusChanged(status);
                }
                return null;
            }
        }).when(mMockConnector).startOtaIfNecessary(Mockito.<OtaStatusChangedCallback>any());

        mController.startOtaUpdatingIfNecessary();
    }

    private EuiccInfo callGetEuiccInfo(final boolean success, final @Nullable EuiccInfo euiccInfo) {
        doAnswer(new Answer<Void>() {
            @Override