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

Commit 44f83523 authored by Cintia Martins's avatar Cintia Martins Committed by Android (Google) Code Review
Browse files

Merge "Unsuspend apps for role revoked on Supervision Service" into main

parents c1d04f4e 752b0246
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)
        }
    }
}