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

Commit 33dd7c0a authored by Sparik Hayrapetyan's avatar Sparik Hayrapetyan Committed by Android (Google) Code Review
Browse files

Merge "[enterprise-esim] Delete managed eSIM when managed profile is deleted" into main

parents 28b54973 f5c81df1
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -402,3 +402,13 @@ flag {
  description: "Add new API for secondary lockscreen"
  bug: "336297680"
}

flag {
  name: "remove_managed_esim_on_work_profile_deletion"
  namespace: "enterprise"
  description: "Remove managed eSIM when work profile is deleted"
  bug: "347925470"
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}
+64 −0
Original line number Diff line number Diff line
@@ -245,6 +245,7 @@ import static android.app.admin.ProvisioningException.ERROR_REMOVE_NON_REQUIRED_
import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED;
import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
import static android.content.Context.RECEIVER_NOT_EXPORTED;
import static android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE;
import static android.content.Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -486,6 +487,7 @@ import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -643,6 +645,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
    static final String ACTION_PROFILE_OFF_DEADLINE =
            "com.android.server.ACTION_PROFILE_OFF_DEADLINE";
    /** Broadcast action invoked when a managed eSIM is removed while deleting work profile. */
    private static final String ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE =
            "com.android.server.ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE";
    /** Extra for the subscription ID of the managed eSIM removed while deleting work profile. */
    private static final String EXTRA_REMOVED_ESIM_SUBSCRIPTION_ID =
            "com.android.server.EXTRA_ESIM_REMOVED_WITH_MANAGED_PROFILE_SUBSCRIPTION_ID";
    private static final String CALLED_FROM_PARENT = "calledFromParent";
    private static final String NOT_CALLED_FROM_PARENT = "notCalledFromParent";
@@ -1266,6 +1276,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                removeCredentialManagementApp(intent.getData().getSchemeSpecificPart());
            } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)) {
                clearWipeProfileNotification();
            } else if (Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
                removeManagedEmbeddedSubscriptionsForUser(userHandle);
            } else if (Intent.ACTION_DATE_CHANGED.equals(action)
                    || Intent.ACTION_TIME_CHANGED.equals(action)) {
                // Update freeze period record when clock naturally progresses to the next day
@@ -1298,6 +1310,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                triggerPolicyComplianceCheckIfNeeded(userHandle, suspended);
            } else if (LOGIN_ACCOUNTS_CHANGED_ACTION.equals(action)) {
                calculateHasIncompatibleAccounts();
            } else if (ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE.equals(action)) {
                int removedSubscriptionId = intent.getIntExtra(EXTRA_REMOVED_ESIM_SUBSCRIPTION_ID,
                        -1);
                Slogf.i(LOG_TAG,
                        "Deleted subscription with ID %d because owning managed profile was "
                                + "removed",
                        removedSubscriptionId);
            }
        }
@@ -2219,9 +2238,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
        filter = new IntentFilter();
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
        filter.addAction(Intent.ACTION_TIME_CHANGED);
        filter.addAction(Intent.ACTION_DATE_CHANGED);
        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
        filter = new IntentFilter();
        filter.addAction(ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE);
        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null,
                mHandler, RECEIVER_NOT_EXPORTED);
        LocalServices.addService(DevicePolicyManagerInternal.class, mLocalService);
@@ -3970,6 +3994,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
            deletedUsers.remove(userInfo.id);
        }
        for (Integer userId : deletedUsers) {
            removeManagedEmbeddedSubscriptionsForUser(userId);
            removeUserData(userId);
            mDevicePolicyEngine.handleUserRemoved(userId);
        }
@@ -8099,6 +8124,45 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PROFILE_WIPED);
    }
    /**
     * Remove eSIM subscriptions that are managed by any of the admin packages of the given
     * userHandle.
     */
    private void removeManagedEmbeddedSubscriptionsForUser(int userHandle) {
        if (!Flags.removeManagedEsimOnWorkProfileDeletion()) {
            return;
        }
        Slogf.i(LOG_TAG,
                "Managed profile with ID=%d deleted: going to remove managed embedded "
                        + "subscriptions", userHandle);
        String profileOwnerPackage = mOwners.getProfileOwnerPackage(userHandle);
        if (profileOwnerPackage == null) {
            Slogf.wtf(LOG_TAG, "Profile owner package for managed profile is null");
            return;
        }
        IntArray managedSubscriptionIds = getSubscriptionIdsInternal(profileOwnerPackage);
        deleteEmbeddedSubscriptions(managedSubscriptionIds);
    }
    private void deleteEmbeddedSubscriptions(IntArray subscriptionIds) {
        EuiccManager euiccManager = mContext.getSystemService(EuiccManager.class);
        for (int subscriptionId : subscriptionIds.toArray()) {
            Slogf.i(LOG_TAG, "Deleting embedded subscription with ID %d", subscriptionId);
            euiccManager.deleteSubscription(subscriptionId,
                    createCallbackPendingIntentForRemovingManagedSubscription(
                            subscriptionId));
        }
    }
    private PendingIntent createCallbackPendingIntentForRemovingManagedSubscription(
            Integer subscriptionId) {
        Intent intent = new Intent(ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE);
        intent.putExtra(EXTRA_REMOVED_ESIM_SUBSCRIPTION_ID, subscriptionId);
        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
    }
    @Override
    public void setFactoryResetProtectionPolicy(ComponentName who, String callerPackageName,
            @Nullable FactoryResetProtectionPolicy policy) {
+42 −0
Original line number Diff line number Diff line
@@ -143,6 +143,7 @@ import android.provider.DeviceConfig;
import android.provider.Settings;
import android.security.KeyChain;
import android.security.keystore.AttestationUtils;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.test.MoreAsserts;
@@ -8715,6 +8716,47 @@ public class DevicePolicyManagerTest extends DpmTestBase {
        }
    }

    @RequiresFlagsEnabled(Flags.FLAG_REMOVE_MANAGED_ESIM_ON_WORK_PROFILE_DELETION)
    @Test
    public void testManagedProfileDeleted_managedEmbeddedSubscriptionDeleted() throws Exception {
        // Setup PO mode.
        setupProfileOwner();
        // Mock SubscriptionManager to return a subscription managed by the profile owner package.
        int managedSubscriptionId = 42;
        SubscriptionInfo managedSubscription = new SubscriptionInfo.Builder().setCardId(1).setId(
                managedSubscriptionId).setGroupOwner(admin1.getPackageName()).build();
        when(getServices().subscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
                List.of(managedSubscription));

        // Send a ACTION_MANAGED_PROFILE_REMOVED broadcast to emulate a managed profile being
        // removed.
        sendBroadcastWithUser(dpms, Intent.ACTION_MANAGED_PROFILE_REMOVED, CALLER_USER_HANDLE);

        // Verify that EuiccManager was called to delete the subscription.
        verify(getServices().euiccManager).deleteSubscription(eq(managedSubscriptionId), any());
    }

    @RequiresFlagsDisabled(Flags.FLAG_REMOVE_MANAGED_ESIM_ON_WORK_PROFILE_DELETION)
    @Test
    public void testManagedProfileDeleted_flagDisabled_managedEmbeddedSubscriptionDeleted()
            throws Exception {
        // Set up PO mode.
        setupProfileOwner();
        // Mock SubscriptionManager to return a subscription managed by the profile owner package.
        int managedSubscriptionId = 42;
        SubscriptionInfo managedSubscription = new SubscriptionInfo.Builder().setCardId(1).setId(
                managedSubscriptionId).setGroupOwner(admin1.getPackageName()).build();
        when(getServices().subscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
                List.of(managedSubscription));

        // Send a ACTION_MANAGED_PROFILE_REMOVED broadcast to emulate a managed profile being
        // removed.
        sendBroadcastWithUser(dpms, Intent.ACTION_MANAGED_PROFILE_REMOVED, CALLER_USER_HANDLE);

        // Verify that EuiccManager was not called to delete the subscription.
        verifyZeroInteractions(getServices().euiccManager);
    }

    private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
        final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage,
                userVpnUid, List.of(new AppOpsManager.OpEntry(
+10 −0
Original line number Diff line number Diff line
@@ -253,6 +253,8 @@ public class DpmMockContext extends MockContext {
                return mMockSystemServices.subscriptionManager;
            case Context.USB_SERVICE:
                return mMockSystemServices.usbManager;
            case Context.EUICC_SERVICE:
                return mMockSystemServices.euiccManager;
        }
        throw new UnsupportedOperationException();
    }
@@ -486,6 +488,14 @@ public class DpmMockContext extends MockContext {
                scheduler);
    }

    @Override
    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
            IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
        mMockSystemServices.registerReceiver(receiver, filter, scheduler);
        return spiedContext.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
                scheduler, flags);
    }

    @Override
    public void unregisterReceiver(BroadcastReceiver receiver) {
        mMockSystemServices.unregisterReceiver(receiver);
+3 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import android.provider.Settings;
import android.security.KeyChain;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.util.ArrayMap;
@@ -151,6 +152,7 @@ public class MockSystemServices {
    public final File dataDir;
    public final PolicyPathProvider pathProvider;
    public final SupervisionManagerInternal supervisionManagerInternal;
    public final EuiccManager euiccManager;

    private final Map<String, PackageState> mTestPackageStates = new ArrayMap<>();

@@ -206,6 +208,7 @@ public class MockSystemServices {
        roleManagerForMock = mock(RoleManagerForMock.class);
        subscriptionManager = mock(SubscriptionManager.class);
        supervisionManagerInternal = mock(SupervisionManagerInternal.class);
        euiccManager = mock(EuiccManager.class);

        // Package manager is huge, so we use a partial mock instead.
        packageManager = spy(realContext.getPackageManager());