Allow switchAfterDownload for org owned devices
Manual testing
Case 1:
1. No exisiting SIM on device
2. COPE PO/DO downloads managed eSIM
3. eSIM gets downloaded successfully and is in an active state.
Case 2:
1. Existing physical SIM present on device
2. COPE PO/DO downloads managed eSIM
3. eSIM gets downloaded successfully and is in active state.
4. Default for data/sms/call remains existing physical SIM and do not change to new managed eSIM.
Case 3:
1. Existing physical SIM and users personal eSIM present on device
2. COPE PO/DO downloads managed eSIM
3. eSIM downloads fails with EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR status code
Bug: 295301164
Test: atest EuiccControllerTest
Change-Id: I7e23c5c290e11fe4f532c0f5621c0099354cdb21
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 18b4b14..2c39076 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -615,25 +615,32 @@
void downloadSubscription(int cardId, int portIndex, DownloadableSubscription subscription,
boolean switchAfterDownload, String callingPackage, boolean forceDeactivateSim,
Bundle resolvedBundle, PendingIntent callbackIntent) {
- boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
- boolean callerCanDownloadAdminManagedSubscription =
- Flags.esimManagementEnabled()
- && callerCanManageDevicePolicyManagedSubscriptions(callingPackage);
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
+
+ boolean callerHasAdminPrivileges = false;
if (Flags.esimManagementEnabled()) {
- if (mContext
- .getSystemService(UserManager.class)
- .hasUserRestriction(UserManager.DISALLOW_SIM_GLOBALLY)
- && !callerCanDownloadAdminManagedSubscription) {
+ callerHasAdminPrivileges = callerCanManageDevicePolicyManagedSubscriptions(
+ callingPackage);
+ if (callerHasAdminPrivileges && (switchAfterDownload && !shouldAllowSwitchAfterDownload(
+ callingPackage))) {
+ // Throw error if calling admin does not have privileges to enable
+ // subscription silently after download but switchAfterDownload is passed as true.
+ sendResult(callbackIntent, ERROR, null);
+ return;
+ }
+ if (mContext.getSystemService(UserManager.class).hasUserRestriction(
+ UserManager.DISALLOW_SIM_GLOBALLY) && !callerHasAdminPrivileges) {
// Only admin managed subscriptions are allowed, but the caller is not authorised to
// download admin managed subscriptions. Abort.
- throw new SecurityException("Caller is not authorized to download subscriptions");
+ sendResult(callbackIntent, ERROR, null);
+ return;
}
}
- mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
// Don't try to resolve the port index for apps which are not targeting on T for backward
// compatibility. instead always use default port 0.
boolean shouldResolvePortIndex = isCompatChangeEnabled(callingPackage,
EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS);
+ boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
long token = Binder.clearCallingIdentity();
try {
@@ -646,26 +653,19 @@
isConsentNeededToResolvePortIndex = (portIndex
== TelephonyManager.INVALID_PORT_INDEX);
}
- // Caller has admin privileges if they can download admin managed subscription,
- // and are not switching the subscription after download (admins cannot silently
- // enable the subscription).
- boolean hasAdminPrivileges =
- callerCanDownloadAdminManagedSubscription && !switchAfterDownload;
Log.d(TAG, " downloadSubscription cardId: " + cardId + " switchAfterDownload: "
- + switchAfterDownload + " portIndex: " + portIndex
- + " forceDeactivateSim: " + forceDeactivateSim + " callingPackage: "
- + callingPackage
+ + switchAfterDownload + " portIndex: " + portIndex + " forceDeactivateSim: "
+ + forceDeactivateSim + " callingPackage: " + callingPackage
+ " isConsentNeededToResolvePortIndex: " + isConsentNeededToResolvePortIndex
+ " shouldResolvePortIndex:" + shouldResolvePortIndex
- + " hasAdminPrivileges:" + hasAdminPrivileges);
- if (!isConsentNeededToResolvePortIndex
- && (callerCanWriteEmbeddedSubscriptions
- || hasAdminPrivileges)) {
+ + " callerHasAdminPrivileges:" + callerHasAdminPrivileges);
+ if (!isConsentNeededToResolvePortIndex && (callerCanWriteEmbeddedSubscriptions
+ || callerHasAdminPrivileges)) {
// With WRITE_EMBEDDED_SUBSCRIPTIONS, we can skip profile-specific permission checks
// and move straight to the profile download.
downloadSubscriptionPrivileged(cardId, portIndex, token, subscription,
switchAfterDownload, forceDeactivateSim, callingPackage, resolvedBundle,
- callbackIntent, callerCanDownloadAdminManagedSubscription,
+ callbackIntent, callerHasAdminPrivileges,
getCurrentEmbeddedSubscriptionIds(cardId));
return;
}
@@ -862,8 +862,11 @@
cardId,
existingSubscriptions);
return;
+ } else if (markAsOwnedByAdmin) {
+ refreshSubscriptionsOwnership(true, callingPackage, cardId,
+ existingSubscriptions);
}
- break;
+ break;
case EuiccService.RESULT_MUST_DEACTIVATE_SIM:
resultCode = RESOLVABLE_ERROR;
addResolutionIntentWithPort(extrasIntent,
@@ -1728,22 +1731,27 @@
SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions(
List.of(mTelephonyManager.getCardIdForDefaultEuicc()),
() -> {
- if (Flags.esimManagementEnabled() && isCallerAdmin) {
- // Mark the newly downloaded subscriptions as being owned by an admin so
- // that actions for that subscription can be restricted,
- // and the admin is limited to effecting only these subscriptions.
- Set<Integer> subscriptionsAfter = getCurrentEmbeddedSubscriptionIds(cardId);
- subscriptionsAfter.removeAll(subscriptionsBefore);
- for (int subId: subscriptionsAfter) {
- SubscriptionManagerService
- .getInstance().setGroupOwner(subId, callingPackage);
- }
- }
+ refreshSubscriptionsOwnership(isCallerAdmin, callingPackage, cardId,
+ subscriptionsBefore);
sendResult(callbackIntent, resultCode, extrasIntent);
});
}
+ private void refreshSubscriptionsOwnership(boolean isCallerAdmin, String callingPackage,
+ int cardId, Set<Integer> subscriptionsBefore) {
+ if (Flags.esimManagementEnabled() && isCallerAdmin) {
+ // Mark the newly downloaded subscriptions as being owned by an admin so
+ // that actions for that subscription can be restricted,
+ // and the admin is limited to effecting only these subscriptions.
+ Set<Integer> subscriptionsAfter = getCurrentEmbeddedSubscriptionIds(cardId);
+ subscriptionsAfter.removeAll(subscriptionsBefore);
+ for (int subId : subscriptionsAfter) {
+ SubscriptionManagerService.getInstance().setGroupOwner(subId, callingPackage);
+ }
+ }
+ }
+
/** Dispatch the given callback intent with the given result code and data. */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public void sendResult(PendingIntent callbackIntent, int resultCode, Intent extrasIntent) {
@@ -2181,20 +2189,31 @@
}
private boolean callerCanManageDevicePolicyManagedSubscriptions(String callingPackage) {
- // isProfileOwner/isDeviceOwner needs to callers user, so create device policy manager
- // with the correct context associated with the caller.
+ DevicePolicyManager devicePolicyManager = getDevicePolicyManager();
+ boolean isAdmin =
+ devicePolicyManager != null && (devicePolicyManager.isProfileOwnerApp(
+ callingPackage)
+ || devicePolicyManager.isDeviceOwnerApp(callingPackage));
+ return isAdmin || mContext.checkCallingOrSelfPermission(
+ Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean shouldAllowSwitchAfterDownload(String callingPackage) {
+ DevicePolicyManager devicePolicyManager = getDevicePolicyManager();
+ return devicePolicyManager != null && (devicePolicyManager.isDeviceOwnerApp(callingPackage)
+ || (devicePolicyManager.isProfileOwnerApp(callingPackage)
+ && devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()));
+ }
+
+ private DevicePolicyManager getDevicePolicyManager() {
+ // create device policy manager with the correct context associated with the caller.
DevicePolicyManager devicePolicyManager =
retrieveDevicePolicyManagerFromUserContext(Binder.getCallingUserHandle());
if (devicePolicyManager == null) {
Log.w(TAG, "Unable to get device policy manager");
- return false;
}
- boolean isAdmin =
- devicePolicyManager.isProfileOwnerApp(callingPackage)
- || devicePolicyManager.isDeviceOwnerApp(callingPackage);
- return isAdmin || mContext.checkCallingOrSelfPermission(
- Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS)
- == PackageManager.PERMISSION_GRANTED;
+ return devicePolicyManager;
}
@Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 673acbc..e5e8d15 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -35,6 +35,7 @@
import android.app.IActivityManager;
import android.app.KeyguardManager;
import android.app.PropertyInvalidatedCache;
+import android.app.admin.DevicePolicyManager;
import android.app.usage.NetworkStatsManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -304,6 +305,7 @@
protected AppOpsManager mAppOpsManager;
protected CarrierConfigManager mCarrierConfigManager;
protected UserManager mUserManager;
+ protected DevicePolicyManager mDevicePolicyManager;
protected KeyguardManager mKeyguardManager;
protected VcnManager mVcnManager;
protected NetworkPolicyManager mNetworkPolicyManager;
@@ -630,6 +632,8 @@
mCarrierConfigManager =
(CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mVcnManager = mContext.getSystemService(VcnManager.class);
mNetworkPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
index 57ae9ed..a6e9fcb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -230,6 +230,7 @@
Settings.Global.EUICC_PROVISIONED, 0);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.EUICC_PROVISIONED, 0);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(false);
}
@After
@@ -894,7 +895,7 @@
@Test
@DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
- public void testDownloadSubscription_adminPermission_usingSwitchAfterDownload()
+ public void testDownloadSubscription_adminPermission_usingSwitchAfterDownload_fails()
throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
setHasWriteEmbeddedPermission(false);
@@ -909,18 +910,99 @@
when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
setCanManageSubscriptionOnTargetSim(false /* isTargetEuicc */, false /* hasPrivileges */);
- callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
+ callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */,
+ true /* complete */,
12345, 0 /* resolvableError */, PACKAGE_NAME /* callingPackage */);
- verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR,
- 0 /* detailedCode */);
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 0 /* detailedCode */);
verify(mMockConnector, never()).downloadSubscription(anyInt(), anyInt(),
any(), anyBoolean(), anyBoolean(), any(), any());
}
@Test
@DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
- public void testDownloadSubscription_onlyAdminManagedAllowed_callerNotAdmin_throws()
+ public void testDownloadSubscription_profileOwner_usingSwitchAfterDownload_fails()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasWriteEmbeddedPermission(false);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(true);
+ setUpUiccSlotData();
+ GetDownloadableSubscriptionMetadataResult result =
+ new GetDownloadableSubscriptionMetadataResult(EuiccService.RESULT_OK,
+ SUBSCRIPTION_WITH_METADATA);
+ doReturn(true).when(mDevicePolicyManager).isProfileOwnerApp(PACKAGE_NAME);
+ doReturn(false).when(mDevicePolicyManager).isOrganizationOwnedDeviceWithManagedProfile();
+ doReturn(false).when(mDevicePolicyManager).isDeviceOwnerApp(PACKAGE_NAME);
+ prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = PACKAGE_NAME;
+ when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
+ setCanManageSubscriptionOnTargetSim(false /* isTargetEuicc */, false /* hasPrivileges */);
+
+ callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */,
+ true /* complete */,
+ 12345, 0 /* resolvableError */, PACKAGE_NAME /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 0 /* detailedCode */);
+ verify(mMockConnector, never()).downloadSubscription(anyInt(), anyInt(), any(),
+ anyBoolean(), anyBoolean(), any(), any());
+ }
+
+ @Test
+ @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+ public void testDownloadSubscription_orgOwnedProfileOwner_usingSwitchAfterDownload_success()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasWriteEmbeddedPermission(false);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(true);
+ setUpUiccSlotData();
+ GetDownloadableSubscriptionMetadataResult result =
+ new GetDownloadableSubscriptionMetadataResult(EuiccService.RESULT_OK,
+ SUBSCRIPTION_WITH_METADATA);
+ doReturn(true).when(mDevicePolicyManager).isProfileOwnerApp(PACKAGE_NAME);
+ doReturn(true).when(mDevicePolicyManager).isOrganizationOwnedDeviceWithManagedProfile();
+ doReturn(false).when(mDevicePolicyManager).isDeviceOwnerApp(PACKAGE_NAME);
+ prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = PACKAGE_NAME;
+ when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
+
+ callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
+ EuiccService.RESULT_OK, 0 /* resolvableError */, PACKAGE_NAME /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
+ assertFalse(mController.mCalledRefreshSubscriptionsAndSendResult);
+ }
+
+ @Test
+ @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+ public void testDownloadSubscription_deviceOwner_usingSwitchAfterDownload_success()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasWriteEmbeddedPermission(false);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(true);
+ setUpUiccSlotData();
+ GetDownloadableSubscriptionMetadataResult result =
+ new GetDownloadableSubscriptionMetadataResult(EuiccService.RESULT_OK,
+ SUBSCRIPTION_WITH_METADATA);
+ doReturn(false).when(mDevicePolicyManager).isProfileOwnerApp(PACKAGE_NAME);
+ doReturn(false).when(mDevicePolicyManager).isOrganizationOwnedDeviceWithManagedProfile();
+ doReturn(true).when(mDevicePolicyManager).isDeviceOwnerApp(PACKAGE_NAME);
+ prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = PACKAGE_NAME;
+ when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
+
+ callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
+ EuiccService.RESULT_OK, 0 /* resolvableError */, PACKAGE_NAME /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
+ assertFalse(mController.mCalledRefreshSubscriptionsAndSendResult);
+ }
+
+ @Test
+ @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+ public void testDownloadSubscription_onlyAdminManagedAllowed_callerNotAdmin_error()
throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
setHasManageDevicePolicyManagedSubscriptionsPermission(false);
@@ -929,15 +1011,10 @@
.when(mUserManager)
.hasUserRestriction(UserManager.DISALLOW_SIM_GLOBALLY);
- assertThrows(SecurityException.class,
- () ->
- callDownloadSubscription(
- SUBSCRIPTION,
- false /* switchAfterDownload */,
- true /* complete */,
- EuiccService.RESULT_OK,
- 0 /* resolvableError */,
- "whatever" /* callingPackage */));
+ callDownloadSubscription(SUBSCRIPTION, false /* switchAfterDownload */, true /* complete */,
+ 12345, 0 /* resolvableError */, "whatever" /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 0 /* detailedCode */);
assertFalse(mController.mCalledRefreshSubscriptionsAndSendResult);
}