Loading src/java/com/android/internal/telephony/euicc/EuiccConnector.java +30 −2 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ */ package com.android.internal.telephony.euicc; import static android.telephony.euicc.EuiccCardManager.ResetOption; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import android.Manifest; Loading Loading @@ -143,6 +145,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection { 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 final int CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS = 113; private static boolean isEuiccCommand(int what) { return what >= CMD_GET_EID; Loading Loading @@ -302,7 +305,9 @@ public class EuiccConnector extends StateMachine implements ServiceConnection { void onUpdateNicknameComplete(int result); } /** Callback class for {@link #eraseSubscriptions}. */ /** * Callback class for {@link #eraseSubscriptions} and {@link #eraseSubscriptionsWithOptions}. */ @VisibleForTesting(visibility = PACKAGE) public interface EraseCommandCallback extends BaseEuiccCommandCallback { /** Called when the erase has completed (though it may have failed). */ Loading Loading @@ -496,12 +501,19 @@ public class EuiccConnector extends StateMachine implements ServiceConnection { sendMessage(CMD_UPDATE_SUBSCRIPTION_NICKNAME, cardId, 0 /* arg2 */, request); } /** Asynchronously erase all profiles on the eUICC. */ /** Asynchronously erase operational profiles on the eUICC. */ @VisibleForTesting(visibility = PACKAGE) public void eraseSubscriptions(int cardId, EraseCommandCallback callback) { sendMessage(CMD_ERASE_SUBSCRIPTIONS, cardId, 0 /* arg2 */, callback); } /** Asynchronously erase specific profiles on the eUICC. */ @VisibleForTesting(visibility = PACKAGE) public void eraseSubscriptionsWithOptions( int cardId, @ResetOption int options, EraseCommandCallback callback) { sendMessage(CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS, cardId, options, callback); } /** Asynchronously ensure that all profiles will be retained on the next factory reset. */ @VisibleForTesting(visibility = PACKAGE) public void retainSubscriptions(int cardId, RetainSubscriptionsCommandCallback callback) { Loading Loading @@ -845,6 +857,21 @@ public class EuiccConnector extends StateMachine implements ServiceConnection { }); break; } case CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS: { mEuiccService.eraseSubscriptionsWithOptions(slotId, message.arg2 /* options */, new IEraseSubscriptionsCallback.Stub() { @Override public void onComplete(int result) { sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> { ((EraseCommandCallback) callback) .onEraseComplete(result); onCommandEnd(callback); }); } }); break; } case CMD_RETAIN_SUBSCRIPTIONS: { mEuiccService.retainSubscriptionsForFactoryReset(slotId, new IRetainSubscriptionsForFactoryResetCallback.Stub() { Loading Loading @@ -936,6 +963,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_ERASE_SUBSCRIPTIONS_WITH_OPTIONS: case CMD_RETAIN_SUBSCRIPTIONS: case CMD_GET_OTA_STATUS: case CMD_START_OTA_IF_NECESSARY: Loading src/java/com/android/internal/telephony/euicc/EuiccController.java +45 −1 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.telephony.TelephonyManager; import android.telephony.UiccAccessRule; import android.telephony.UiccCardInfo; import android.telephony.euicc.DownloadableSubscription; import android.telephony.euicc.EuiccCardManager.ResetOption; import android.telephony.euicc.EuiccInfo; import android.telephony.euicc.EuiccManager; import android.telephony.euicc.EuiccManager.OtaStatus; Loading Loading @@ -957,7 +958,50 @@ public class EuiccController extends IEuiccController.Stub { } long token = Binder.clearCallingIdentity(); try { mConnector.eraseSubscriptions(cardId, new EuiccConnector.EraseCommandCallback() { mConnector.eraseSubscriptions( cardId, new EuiccConnector.EraseCommandCallback() { @Override public void onEraseComplete(int result) { Intent extrasIntent = new Intent(); final int resultCode; switch (result) { case EuiccService.RESULT_OK: resultCode = OK; refreshSubscriptionsAndSendResult( callbackIntent, resultCode, extrasIntent); return; 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); } } @Override public void eraseSubscriptionsWithOptions( int cardId, @ResetOption int options, PendingIntent callbackIntent) { if (!callerCanWriteEmbeddedSubscriptions()) { throw new SecurityException( "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to erase subscriptions"); } long token = Binder.clearCallingIdentity(); try { mConnector.eraseSubscriptionsWithOptions( cardId, options, new EuiccConnector.EraseCommandCallback() { @Override public void onEraseComplete(int result) { Intent extrasIntent = new Intent(); Loading tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java +51 −2 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.internal.telephony.euicc; import static android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES; import static android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE; import static org.junit.Assert.assertEquals; Loading Loading @@ -984,8 +985,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testEraseSubscriptions_error() throws Exception { setHasWriteEmbeddedPermission(true); callEraseSubscriptions(true /* complete */, 42 /* result */); verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 42 /* detailedCode */); verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 42 /* detailedCode */); } @Test Loading @@ -996,6 +996,36 @@ public class EuiccControllerTest extends TelephonyTest { assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult); } @Test(expected = SecurityException.class) public void testEraseSubscriptionsWithOptions_noPrivileges() throws Exception { setHasWriteEmbeddedPermission(false); callEraseSubscriptionsWithOptions(false /* complete */, 0 /* result */); } @Test public void testEraseSubscriptionsWithOptions_serviceUnavailable() throws Exception { setHasWriteEmbeddedPermission(true); callEraseSubscriptionsWithOptions(false /* complete */, 0 /* result */); verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 0 /* detailedCode */); verify(mMockConnector).eraseSubscriptionsWithOptions(anyInt(), anyInt(), any()); } @Test public void testEraseSubscriptionsWithOptions_error() throws Exception { setHasWriteEmbeddedPermission(true); callEraseSubscriptionsWithOptions(true /* complete */, 42 /* result */); verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 42 /* detailedCode */); } @Test public void testEraseSubscriptionsWithOptions_success() throws Exception { setHasWriteEmbeddedPermission(true); callEraseSubscriptionsWithOptions(true /* complete */, EuiccService.RESULT_OK); verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */); assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult); } @Test(expected = SecurityException.class) public void testRetainSubscriptionsForFactoryReset_noPrivileges() throws Exception { setHasMasterClearPermission(false); Loading Loading @@ -1318,6 +1348,25 @@ public class EuiccControllerTest extends TelephonyTest { mController.eraseSubscriptions(CARD_ID, resultCallback); } private void callEraseSubscriptionsWithOptions(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.EraseCommandCallback cb = invocation .getArgument(2 /* resultCallback */); if (complete) { cb.onEraseComplete(result); } else { cb.onEuiccServiceUnavailable(); } return null; } }).when(mMockConnector).eraseSubscriptionsWithOptions(anyInt(), anyInt(), any()); mController.eraseSubscriptionsWithOptions(CARD_ID, RESET_OPTION_DELETE_OPERATIONAL_PROFILES, resultCallback); } private void callRetainSubscriptionsForFactoryReset(final boolean complete, final int result) { PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0); doAnswer(new Answer<Void>() { Loading Loading
src/java/com/android/internal/telephony/euicc/EuiccConnector.java +30 −2 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ */ package com.android.internal.telephony.euicc; import static android.telephony.euicc.EuiccCardManager.ResetOption; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import android.Manifest; Loading Loading @@ -143,6 +145,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection { 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 final int CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS = 113; private static boolean isEuiccCommand(int what) { return what >= CMD_GET_EID; Loading Loading @@ -302,7 +305,9 @@ public class EuiccConnector extends StateMachine implements ServiceConnection { void onUpdateNicknameComplete(int result); } /** Callback class for {@link #eraseSubscriptions}. */ /** * Callback class for {@link #eraseSubscriptions} and {@link #eraseSubscriptionsWithOptions}. */ @VisibleForTesting(visibility = PACKAGE) public interface EraseCommandCallback extends BaseEuiccCommandCallback { /** Called when the erase has completed (though it may have failed). */ Loading Loading @@ -496,12 +501,19 @@ public class EuiccConnector extends StateMachine implements ServiceConnection { sendMessage(CMD_UPDATE_SUBSCRIPTION_NICKNAME, cardId, 0 /* arg2 */, request); } /** Asynchronously erase all profiles on the eUICC. */ /** Asynchronously erase operational profiles on the eUICC. */ @VisibleForTesting(visibility = PACKAGE) public void eraseSubscriptions(int cardId, EraseCommandCallback callback) { sendMessage(CMD_ERASE_SUBSCRIPTIONS, cardId, 0 /* arg2 */, callback); } /** Asynchronously erase specific profiles on the eUICC. */ @VisibleForTesting(visibility = PACKAGE) public void eraseSubscriptionsWithOptions( int cardId, @ResetOption int options, EraseCommandCallback callback) { sendMessage(CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS, cardId, options, callback); } /** Asynchronously ensure that all profiles will be retained on the next factory reset. */ @VisibleForTesting(visibility = PACKAGE) public void retainSubscriptions(int cardId, RetainSubscriptionsCommandCallback callback) { Loading Loading @@ -845,6 +857,21 @@ public class EuiccConnector extends StateMachine implements ServiceConnection { }); break; } case CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS: { mEuiccService.eraseSubscriptionsWithOptions(slotId, message.arg2 /* options */, new IEraseSubscriptionsCallback.Stub() { @Override public void onComplete(int result) { sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> { ((EraseCommandCallback) callback) .onEraseComplete(result); onCommandEnd(callback); }); } }); break; } case CMD_RETAIN_SUBSCRIPTIONS: { mEuiccService.retainSubscriptionsForFactoryReset(slotId, new IRetainSubscriptionsForFactoryResetCallback.Stub() { Loading Loading @@ -936,6 +963,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_ERASE_SUBSCRIPTIONS_WITH_OPTIONS: case CMD_RETAIN_SUBSCRIPTIONS: case CMD_GET_OTA_STATUS: case CMD_START_OTA_IF_NECESSARY: Loading
src/java/com/android/internal/telephony/euicc/EuiccController.java +45 −1 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.telephony.TelephonyManager; import android.telephony.UiccAccessRule; import android.telephony.UiccCardInfo; import android.telephony.euicc.DownloadableSubscription; import android.telephony.euicc.EuiccCardManager.ResetOption; import android.telephony.euicc.EuiccInfo; import android.telephony.euicc.EuiccManager; import android.telephony.euicc.EuiccManager.OtaStatus; Loading Loading @@ -957,7 +958,50 @@ public class EuiccController extends IEuiccController.Stub { } long token = Binder.clearCallingIdentity(); try { mConnector.eraseSubscriptions(cardId, new EuiccConnector.EraseCommandCallback() { mConnector.eraseSubscriptions( cardId, new EuiccConnector.EraseCommandCallback() { @Override public void onEraseComplete(int result) { Intent extrasIntent = new Intent(); final int resultCode; switch (result) { case EuiccService.RESULT_OK: resultCode = OK; refreshSubscriptionsAndSendResult( callbackIntent, resultCode, extrasIntent); return; 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); } } @Override public void eraseSubscriptionsWithOptions( int cardId, @ResetOption int options, PendingIntent callbackIntent) { if (!callerCanWriteEmbeddedSubscriptions()) { throw new SecurityException( "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to erase subscriptions"); } long token = Binder.clearCallingIdentity(); try { mConnector.eraseSubscriptionsWithOptions( cardId, options, new EuiccConnector.EraseCommandCallback() { @Override public void onEraseComplete(int result) { Intent extrasIntent = new Intent(); Loading
tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java +51 −2 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.internal.telephony.euicc; import static android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES; import static android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE; import static org.junit.Assert.assertEquals; Loading Loading @@ -984,8 +985,7 @@ public class EuiccControllerTest extends TelephonyTest { public void testEraseSubscriptions_error() throws Exception { setHasWriteEmbeddedPermission(true); callEraseSubscriptions(true /* complete */, 42 /* result */); verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 42 /* detailedCode */); verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 42 /* detailedCode */); } @Test Loading @@ -996,6 +996,36 @@ public class EuiccControllerTest extends TelephonyTest { assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult); } @Test(expected = SecurityException.class) public void testEraseSubscriptionsWithOptions_noPrivileges() throws Exception { setHasWriteEmbeddedPermission(false); callEraseSubscriptionsWithOptions(false /* complete */, 0 /* result */); } @Test public void testEraseSubscriptionsWithOptions_serviceUnavailable() throws Exception { setHasWriteEmbeddedPermission(true); callEraseSubscriptionsWithOptions(false /* complete */, 0 /* result */); verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 0 /* detailedCode */); verify(mMockConnector).eraseSubscriptionsWithOptions(anyInt(), anyInt(), any()); } @Test public void testEraseSubscriptionsWithOptions_error() throws Exception { setHasWriteEmbeddedPermission(true); callEraseSubscriptionsWithOptions(true /* complete */, 42 /* result */); verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 42 /* detailedCode */); } @Test public void testEraseSubscriptionsWithOptions_success() throws Exception { setHasWriteEmbeddedPermission(true); callEraseSubscriptionsWithOptions(true /* complete */, EuiccService.RESULT_OK); verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */); assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult); } @Test(expected = SecurityException.class) public void testRetainSubscriptionsForFactoryReset_noPrivileges() throws Exception { setHasMasterClearPermission(false); Loading Loading @@ -1318,6 +1348,25 @@ public class EuiccControllerTest extends TelephonyTest { mController.eraseSubscriptions(CARD_ID, resultCallback); } private void callEraseSubscriptionsWithOptions(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.EraseCommandCallback cb = invocation .getArgument(2 /* resultCallback */); if (complete) { cb.onEraseComplete(result); } else { cb.onEuiccServiceUnavailable(); } return null; } }).when(mMockConnector).eraseSubscriptionsWithOptions(anyInt(), anyInt(), any()); mController.eraseSubscriptionsWithOptions(CARD_ID, RESET_OPTION_DELETE_OPERATIONAL_PROFILES, resultCallback); } private void callRetainSubscriptionsForFactoryReset(final boolean complete, final int result) { PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0); doAnswer(new Answer<Void>() { Loading