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

Commit 752b0246 authored by Cintia Martins's avatar Cintia Martins
Browse files

Unsuspend apps for role revoked on Supervision Service

Bug: 430560580
Test: atest SupervisionServiceTest
Test: atest CtsSupervisionTestCases
Flag: android.app.supervision.flags.enable_supervision_app_service
Change-Id: I4f4b0631613b9985ca5f956ed43f3c9bd5057a46
parent 8a414be0
Loading
Loading
Loading
Loading
+46 −12
Original line number Diff line number Diff line
@@ -69,7 +69,6 @@ import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FunctionalUtils.RemoteExceptionIgnoringConsumer;
import com.android.internal.util.IndentingPrintWriter;
@@ -491,11 +490,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
            List<String> supervisionPackagesPerRole =
                    mInjector.getRoleHoldersAsUser(role, UserHandle.of(userId));
            supervisionPackages.addAll(supervisionPackagesPerRole);
            // TODO(b/432705581): Consider adding a method that takes a list of packages to clear
            // suspension for, instead of calling for each package in a loop.
            for (String supervisionPackage : supervisionPackagesPerRole) {
                clearSuspendedPackagesFor(userId, supervisionPackage, role);
            }
            clearSuspendedPackagesFor(userId, supervisionPackagesPerRole, role);
        }

        DevicePolicyManagerInternal dpmi = mInjector.getDpmInternal();
@@ -512,13 +507,17 @@ public class SupervisionService extends ISupervisionManager.Stub {
        }
    }

    private void clearSuspendedPackagesFor(int userId, String supervisionPackage, String roleName) {
    private void clearSuspendedPackagesFor(int userId, List<String> supervisionPackages,
            @Nullable String role) {
        PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
        for (String supervisionPackage: supervisionPackages) {
            if (pmi != null) {
                pmi.unsuspendForSuspendingPackage(supervisionPackage, userId, userId);
            }

        mInjector.removeRoleHoldersAsUser(roleName, supervisionPackage, UserHandle.of(userId));
            if (RoleManager.ROLE_SUPERVISION.equals(role)) {
                mInjector.removeRoleHoldersAsUser(role, supervisionPackage, UserHandle.of(userId));
            }
        }
    }

    private void maybeApplyUserRestrictions() {
@@ -659,6 +658,37 @@ public class SupervisionService extends ISupervisionManager.Stub {
        return UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID);
    }

    private void updateSupervisionRoleHolders(@UserIdInt int userId) {
        List<String> roleHolders =
                mInjector.getRoleHoldersAsUser(RoleManager.ROLE_SUPERVISION, UserHandle.of(userId));

        synchronized (getLockObject()) {
            SupervisionUserData data = getUserDataLocked(userId);
            data.supervisionRoleHolders.clear();
            data.supervisionRoleHolders.addAll(roleHolders);
            if (Flags.persistentSupervisionSettings()) {
                mSupervisionSettings.saveUserData();
            }
        }
    }

    private List<String> getRemovedSupervisionRoleHolders(@UserIdInt int userId) {
        List<String> newRoleHolders =
                mInjector.getRoleHoldersAsUser(RoleManager.ROLE_SUPERVISION, UserHandle.of(userId));

        synchronized (getLockObject()) {
            SupervisionUserData data = getUserDataLocked(userId);
            List<String> removedRoleHolders = new ArrayList<>(data.supervisionRoleHolders);
            removedRoleHolders.removeAll(newRoleHolders);
            data.supervisionRoleHolders.clear();
            data.supervisionRoleHolders.addAll(newRoleHolders);
            if (Flags.persistentSupervisionSettings()) {
                mSupervisionSettings.saveUserData();
            }
            return removedRoleHolders;
        }
    }

    /** Provides local services in a lazy manner. */
    static class Injector {
        public Context context;
@@ -799,6 +829,7 @@ public class SupervisionService extends ISupervisionManager.Stub {

        @Override
        public void onUserStarting(@NonNull TargetUser user) {
            mSupervisionService.updateSupervisionRoleHolders(user.getUserIdentifier());
            if (Flags.enableSyncWithDpm() && !user.isPreCreated()) {
                mSupervisionService.syncStateWithDevicePolicyManager(user.getUserIdentifier());
            }
@@ -869,13 +900,16 @@ public class SupervisionService extends ISupervisionManager.Stub {

        void register() {
            mInjector.addOnRoleHoldersChangedListenerAsUser(
                    BackgroundThread.getExecutor(), this, UserHandle.ALL);
                    mServiceThread.getThreadExecutor(), this, UserHandle.ALL);
        }

        @Override
        public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
            if (RoleManager.ROLE_SUPERVISION.equals(roleName)) {
                maybeApplyUserRestrictionsFor(user);
                List<String> removedRoleHolders =
                        getRemovedSupervisionRoleHolders(user.getIdentifier());
                clearSuspendedPackagesFor(user.getIdentifier(), removedRoleHolders, null);
            }
        }
    }
+21 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.UserIdInt;
import android.app.supervision.SupervisionRecoveryInfo;
import android.os.Environment;
import android.os.PersistableBundle;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
@@ -62,6 +63,8 @@ public class SupervisionSettings {
    private static final String KEY_USER_ID = "user_id";
    private static final String KEY_ENABLED = "supervision_enabled";
    private static final String KEY_APP_PACKAGE = "supervision_app_package";
    private static final String KEY_ROLE_HOLDERS_LIST = "supervision_role_holders_list";
    private static final String KEY_ROLE_HOLDER = "supervision_role_holder";
    private static final String KEY_LOCK_SCREEN_ENABLED = "supervision_lockscreen_enabled";
    private static final String KEY_LOCK_SCREEN_OPTIONS = "supervision_lockscreen_options";

@@ -138,6 +141,7 @@ public class SupervisionSettings {
                if (parser.getName().equals(PREF_USER_DATA)) {
                    int userId = parser.getAttributeInt(null, KEY_USER_ID);
                    SupervisionUserData data = getUserData(userId);
                    data.supervisionRoleHolders = new ArraySet<>();
                    data.supervisionEnabled = parser.getAttributeBoolean(null, KEY_ENABLED);
                    data.supervisionAppPackage = parser.getAttributeValue(null, KEY_APP_PACKAGE);
                    if (data.supervisionAppPackage.isEmpty()) {
@@ -149,6 +153,14 @@ public class SupervisionSettings {
                        if (parser.getName().equals(KEY_LOCK_SCREEN_OPTIONS)) {
                            data.supervisionLockScreenOptions =
                                    PersistableBundle.restoreFromXml(parser);
                        } else if (parser.getName().equals(KEY_ROLE_HOLDERS_LIST)) {
                            final int roleHoldersDepth = parser.getDepth();
                            while (XmlUtils.nextElementWithin(parser, roleHoldersDepth)) {
                                if (parser.getName().equals(KEY_ROLE_HOLDER)) {
                                    String roleHolder = parser.getAttributeValue(null, "package");
                                    data.supervisionRoleHolders.add(roleHolder);
                                }
                            }
                        }
                    }
                }
@@ -179,6 +191,15 @@ public class SupervisionSettings {
                        data.supervisionAppPackage == null ? "" : data.supervisionAppPackage);
                xml.attributeBoolean(
                        null, KEY_LOCK_SCREEN_ENABLED, data.supervisionLockScreenEnabled);
                if (data.supervisionRoleHolders != null && !data.supervisionRoleHolders.isEmpty()) {
                    xml.startTag(null, KEY_ROLE_HOLDERS_LIST);
                    for (String roleHolder : data.supervisionRoleHolders) {
                        xml.startTag(null, KEY_ROLE_HOLDER);
                        xml.attribute(null, "package", roleHolder);
                        xml.endTag(null, KEY_ROLE_HOLDER);
                    }
                    xml.endTag(null, KEY_ROLE_HOLDERS_LIST);
                }
                if (data.supervisionLockScreenOptions != null) {
                    xml.startTag(null, KEY_LOCK_SCREEN_OPTIONS);
                    data.supervisionLockScreenOptions.saveToXml(xml);
+3 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.PersistableBundle;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;

/** User specific data, used internally by the {@link SupervisionService}. */
@@ -29,6 +30,7 @@ public class SupervisionUserData {
    @Nullable public String supervisionAppPackage;
    public boolean supervisionLockScreenEnabled;
    @Nullable public PersistableBundle supervisionLockScreenOptions;
    ArraySet<String> supervisionRoleHolders = new ArraySet<>();

    public SupervisionUserData(@UserIdInt int userId) {
        this.userId = userId;
@@ -40,6 +42,7 @@ public class SupervisionUserData {
        pw.increaseIndent();
        pw.println("supervisionEnabled: " + supervisionEnabled);
        pw.println("supervisionAppPackage: " + supervisionAppPackage);
        pw.println("supervisionRoleHolders: " + supervisionRoleHolders);
        pw.println("supervisionLockScreenEnabled: " + supervisionLockScreenEnabled);
        pw.println("supervisionLockScreenOptions: " + supervisionLockScreenOptions);
        pw.decreaseIndent();
+24 −3
Original line number Diff line number Diff line
@@ -383,8 +383,9 @@ class SupervisionServiceTest {
            UserHandle.of(USER_ID),
            listOf("com.example.supervisionapp1"),
        )
        setSupervisionRecoveryInfo(state = STATE_VERIFIED)
        clearInvocations(mockDpmInternal)
        setSupervisionEnabledForUser(USER_ID, true)
        setSupervisionRecoveryInfo(state = STATE_VERIFIED)

        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
        verify(mockDpmInternal)
@@ -562,12 +563,13 @@ class SupervisionServiceTest {
    @RequiresFlagsEnabled(Flags.FLAG_PERSISTENT_SUPERVISION_SETTINGS)
    fun setSupervisionRecoveryInfo_supervisionEnabled_hasSupervisionRoleHolders_doesNotRestrictFactoryReset() {
        addDefaultAndTestUsers()
        setSupervisionEnabledForUser(USER_ID, true)
        injector.setRoleHoldersAsUser(
            RoleManager.ROLE_SUPERVISION,
            UserHandle.of(USER_ID),
            listOf("com.example.supervisionapp1"),
        )
        clearInvocations(mockDpmInternal)
        setSupervisionEnabledForUser(USER_ID, true)
        setSupervisionRecoveryInfo(state = STATE_VERIFIED)

        // Once for the initial setSupervisionEnabledForUser, and again for the
@@ -636,6 +638,21 @@ class SupervisionServiceTest {
        }
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_REMOVE_POLICIES_ON_SUPERVISION_DISABLE)
    fun onRoleHoldersChanged_removesPackageSuspensionForRemovedRoleHolder() {
        val packageName = "com.example.supervisionapp"
        val roleName = RoleManager.ROLE_SUPERVISION
        injector.setRoleHoldersAsUser(roleName, UserHandle.of(USER_ID), listOf(packageName,
            "com.example.supervisionapp2", "com.example.supervisionapp3"))

        injector.setRoleHoldersAsUser(roleName, UserHandle.of(USER_ID), emptyList())
        injector.awaitServiceThreadIdle()

        verify(mockPackageManagerInternal)
            .unsuspendForSuspendingPackage(eq(packageName), eq(USER_ID), eq(USER_ID))
    }

    private fun registerSupervisionListenerForUser(
        userId: Int
    ): Pair<ISupervisionListener, IBinder> {
@@ -814,12 +831,15 @@ typealias SupervisionListenerMap = Map<Int, Pair<ISupervisionListener, IBinder>>
private class TestInjector(val context: Context, private val serviceThread: ServiceThread) :
    SupervisionService.Injector(context) {
    private val roleHolders = mutableMapOf<Pair<String, UserHandle>, List<String>>()
    private var roleHoldersChangedListener: OnRoleHoldersChangedListener? = null

    override fun addOnRoleHoldersChangedListenerAsUser(
        executor: Executor,
        listener: OnRoleHoldersChangedListener,
        user: UserHandle,
    ) {}
    ) {
        this.roleHoldersChangedListener = listener
    }

    override fun getRoleHoldersAsUser(roleName: String, user: UserHandle): List<String> {
        return roleHolders[Pair(roleName, user)] ?: emptyList()
@@ -827,6 +847,7 @@ private class TestInjector(val context: Context, private val serviceThread: Serv

    fun setRoleHoldersAsUser(roleName: String, user: UserHandle, packages: List<String>) {
        roleHolders[Pair(roleName, user)] = packages
        roleHoldersChangedListener?.onRoleHoldersChanged(roleName, user)
    }

    override fun getServiceThread(): ServiceThread {
+23 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.app.supervision.flags.Flags
import android.os.PersistableBundle
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.util.ArraySet
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import java.nio.file.Files
@@ -144,6 +145,24 @@ class SupervisionSettingsTest {
        mSupervisionSettings.getUserData(1).checkUserData(true, "package1", true, null)
    }

    @Test
    fun saveAndLoadSupervisionUserData_hasSupervisionRoleHolders_retrievesUserDataCorrectly() {
        // Get and set user data
        val userData1 = mSupervisionSettings.getUserData(1)
        val roleHolders = ArraySet<String>(setOf("package2", "package3", "package4"))
        userData1.changeUserData(true, "package1", true,
            null, roleHolders)

        // Save, change and load user data
        mSupervisionSettings.saveUserData()
        userData1.changeUserData(false, null, false, BUNDLE_2)
        mSupervisionSettings.loadUserData()

        // Check if user data was loaded correctly
        mSupervisionSettings.getUserData(1).checkUserData(true, "package1",
            true, null, roleHolders)
    }

    @Test
    fun saveAndLoadSupervisionUserData_manyUsers_retrievesUserDataCorrectly() {
        // Get and set user data
@@ -218,11 +237,13 @@ class SupervisionSettingsTest {
            appPackage: String?,
            lockScreenEnabled: Boolean,
            lockScreenOptions: PersistableBundle?,
            roleHolders: ArraySet<String> = ArraySet<String>(),
        ) {
            this.supervisionEnabled = enabled
            this.supervisionAppPackage = appPackage
            this.supervisionLockScreenEnabled = lockScreenEnabled
            this.supervisionLockScreenOptions = lockScreenOptions
            this.supervisionRoleHolders = roleHolders
        }

        fun SupervisionUserData.checkUserData(
@@ -230,6 +251,7 @@ class SupervisionSettingsTest {
            appPackage: String?,
            lockScreenEnabled: Boolean,
            lockScreenOptions: PersistableBundle?,
            roleHolders: ArraySet<String> = ArraySet<String>(),
        ) {
            assertThat(this.supervisionEnabled).isEqualTo(enabled)
            assertThat(this.supervisionAppPackage).isEqualTo(appPackage)
@@ -240,6 +262,7 @@ class SupervisionSettingsTest {
                assertThat(this.supervisionLockScreenOptions.toString())
                    .isEqualTo(lockScreenOptions.toString())
            }
            assertThat(this.supervisionRoleHolders).containsExactlyElementsIn(roleHolders)
        }
    }
}