Loading src/java/com/android/internal/telephony/euicc/EuiccConnector.java +41 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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(); Loading Loading @@ -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; Loading src/java/com/android/internal/telephony/euicc/EuiccController.java +32 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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(); Loading tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java +54 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -151,6 +156,11 @@ public class EuiccControllerTest extends TelephonyTest { mCalledRefreshSubscriptionsAndSendResult = true; sendResult(callbackIntent, resultCode, extrasIntent); } @Override public void sendOtaStatusChangedBroadcast() { ++mNumOtaStatusChanged; } } @Before Loading Loading @@ -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); Loading Loading @@ -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 Loading Loading
src/java/com/android/internal/telephony/euicc/EuiccConnector.java +41 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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(); Loading Loading @@ -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; Loading
src/java/com/android/internal/telephony/euicc/EuiccController.java +32 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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(); Loading
tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java +54 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -151,6 +156,11 @@ public class EuiccControllerTest extends TelephonyTest { mCalledRefreshSubscriptionsAndSendResult = true; sendResult(callbackIntent, resultCode, extrasIntent); } @Override public void sendOtaStatusChangedBroadcast() { ++mNumOtaStatusChanged; } } @Before Loading Loading @@ -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); Loading Loading @@ -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 Loading