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

Commit ca8cd83a authored by Vitor Carvalho's avatar Vitor Carvalho
Browse files

Update DPMS to ensure that supervision is enabled in SupervisionService when...

Update DPMS to ensure that supervision is enabled in SupervisionService when it detects that the supervision component has profile owner status on start.

Test: atest SupervisionServiceTest
Bug: 376213673
Flag: android.app.admin.flags.enable_supervision_service_sync
Change-Id: I8575402ca9f71cac2808de3fb0c5e39a3c5cac90
parent 6cbb391b
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -34,6 +34,14 @@ public abstract class SupervisionManagerInternal {
     */
    public abstract boolean isSupervisionEnabledForUser(@UserIdInt int userId);

    /**
     * Set whether supervision is enabled for the specified user.
     *
     * @param userId The user to set the supervision state for
     * @param enabled Whether or not the user should be supervised
     */
    public abstract void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled);

    /**
     * Sets whether the supervision lock screen should be shown for the specified user
     *
+47 −11
Original line number Diff line number Diff line
@@ -19,7 +19,9 @@ package com.android.server.supervision;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.supervision.ISupervisionManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Bundle;
@@ -28,19 +30,19 @@ import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.SparseArray;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
import com.android.server.pm.UserManagerInternal;

import java.io.FileDescriptor;
import java.io.PrintWriter;

/**
 * Service for handling system supervision.
 */
/** Service for handling system supervision. */
public class SupervisionService extends ISupervisionManager.Stub {
    private static final String LOG_TAG = "SupervisionService";

@@ -52,14 +54,25 @@ public class SupervisionService extends ISupervisionManager.Stub {
    @GuardedBy("getLockObject()")
    private final SparseArray<SupervisionUserData> mUserData = new SparseArray<>();

    private final DevicePolicyManagerInternal mDpmInternal;
    private final UserManagerInternal mUserManagerInternal;

    public SupervisionService(Context context) {
        mContext = context.createAttributionContext(LOG_TAG);
        mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
        mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
    }

    void syncStateWithDevicePolicyManager(TargetUser user) {
        if (user.isPreCreated()) return;

        // Ensure that supervision is enabled when supervision app is the profile owner.
        if (android.app.admin.flags.Flags.enableSupervisionServiceSync() && isProfileOwner(user)) {
            setSupervisionEnabledForUser(user.getUserIdentifier(), true);
        }
    }

    @Override
    public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
        synchronized (getLockObject()) {
@@ -74,14 +87,15 @@ public class SupervisionService extends ISupervisionManager.Stub {
            @Nullable FileDescriptor err,
            @NonNull String[] args,
            @Nullable ShellCallback callback,
            @NonNull ResultReceiver resultReceiver) throws RemoteException {
            @NonNull ResultReceiver resultReceiver)
            throws RemoteException {
        new SupervisionServiceShellCommand(this)
                .exec(this, in, out, err, args, callback, resultReceiver);
    }

    @Override
    protected void dump(@NonNull FileDescriptor fd,
            @NonNull PrintWriter printWriter, @Nullable String[] args) {
    protected void dump(
            @NonNull FileDescriptor fd, @NonNull PrintWriter printWriter, @Nullable String[] args) {
        if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, printWriter)) return;

        try (var pw = new IndentingPrintWriter(printWriter, "  ")) {
@@ -120,6 +134,17 @@ public class SupervisionService extends ISupervisionManager.Stub {
        }
    }

    /** Returns whether the supervision app has profile owner status. */
    private boolean isProfileOwner(TargetUser user) {
        ComponentName profileOwner = mDpmInternal.getProfileOwnerAsUser(user.getUserIdentifier());
        if (profileOwner == null) {
            return false;
        }

        String configPackage = mContext.getResources().getString(R.string.config_systemSupervision);
        return profileOwner.getPackageName().equals(configPackage);
    }

    public static class Lifecycle extends SystemService {
        private final SupervisionService mSupervisionService;

@@ -133,13 +158,24 @@ public class SupervisionService extends ISupervisionManager.Stub {
            publishLocalService(SupervisionManagerInternal.class, mSupervisionService.mInternal);
            publishBinderService(Context.SUPERVISION_SERVICE, mSupervisionService);
        }

        @Override
        public void onUserStarting(@NonNull TargetUser user) {
            mSupervisionService.syncStateWithDevicePolicyManager(user);
        }
    }

    final SupervisionManagerInternal mInternal = new SupervisionManagerInternal() {
    final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl();

    private final class SupervisionManagerInternalImpl extends SupervisionManagerInternal {
        @Override
        public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
            synchronized (getLockObject()) {
                return getUserDataLocked(userId).supervisionEnabled;
            return SupervisionService.this.isSupervisionEnabledForUser(userId);
        }

        @Override
        public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
            SupervisionService.this.setSupervisionEnabledForUser(userId, enabled);
        }

        @Override
@@ -151,7 +187,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
                data.supervisionLockScreenOptions = options;
            }
        }
    };
    }

    private final class UserLifecycleListener implements UserManagerInternal.UserLifecycleListener {
        @Override
+82 −14
Original line number Diff line number Diff line
@@ -16,12 +16,20 @@

package com.android.server.supervision

import android.app.admin.DevicePolicyManagerInternal
import android.content.ComponentName
import android.content.Context
import android.content.pm.UserInfo
import android.os.Bundle
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.R
import com.android.server.LocalServices
import com.android.server.SystemService.TargetUser
import com.android.server.pm.UserManagerInternal
import com.google.common.truth.Truth.assertThat
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -29,11 +37,12 @@ import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.kotlin.whenever

/**
 * Unit tests for {@link SupervisionService}.
 * <p/>
 * Run with <code>atest SupervisionServiceTest</code>.
 * Unit tests for [SupervisionService].
 *
 * Run with `atest SupervisionServiceTest`.
 */
@RunWith(AndroidJUnit4::class)
class SupervisionServiceTest {
@@ -41,18 +50,21 @@ class SupervisionServiceTest {
        const val USER_ID = 100
    }

    private lateinit var service: SupervisionService
    @get:Rule val mocks: MockitoRule = MockitoJUnit.rule()
    @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()

    @Rule
    @JvmField
    val mocks: MockitoRule = MockitoJUnit.rule()
    @Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal
    @Mock private lateinit var mockUserManagerInternal: UserManagerInternal

    @Mock
    private lateinit var mockUserManagerInternal: UserManagerInternal
    private lateinit var context: Context
    private lateinit var service: SupervisionService

    @Before
    fun setup() {
        val context = InstrumentationRegistry.getInstrumentation().context
    fun setUp() {
        context = InstrumentationRegistry.getInstrumentation().context

        LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java)
        LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal)

        LocalServices.removeServiceForTest(UserManagerInternal::class.java)
        LocalServices.addService(UserManagerInternal::class.java, mockUserManagerInternal)
@@ -61,7 +73,46 @@ class SupervisionServiceTest {
    }

    @Test
    fun testSetSupervisionEnabledForUser() {
    @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC)
    fun syncStateWithDevicePolicyManager_supervisionAppIsProfileOwner_enablesSupervision() {
        val supervisionPackageName =
            context.getResources().getString(R.string.config_systemSupervision)

        whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
            .thenReturn(ComponentName(supervisionPackageName, "MainActivity"))

        service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID))

        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
    }

    @Test
    @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC)
    fun syncStateWithDevicePolicyManager_userPreCreated_doesNotEnableSupervision() {
        val supervisionPackageName =
            context.getResources().getString(R.string.config_systemSupervision)

        whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
            .thenReturn(ComponentName(supervisionPackageName, "MainActivity"))

        service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID, preCreated = true))

        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
    }

    @Test
    @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC)
    fun syncStateWithDevicePolicyManager_supervisionAppIsNotProfileOwner_doesNotEnableSupervision() {
        whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
            .thenReturn(ComponentName("other.package", "MainActivity"))

        service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID))

        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
    }

    @Test
    fun setSupervisionEnabledForUser() {
        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()

        service.setSupervisionEnabledForUser(USER_ID, true)
@@ -72,7 +123,18 @@ class SupervisionServiceTest {
    }

    @Test
    fun testSetSupervisionLockscreenEnabledForUser() {
    fun supervisionEnabledForUser_internal() {
        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()

        service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()

        service.mInternal.setSupervisionEnabledForUser(USER_ID, false)
        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
    }

    @Test
    fun setSupervisionLockscreenEnabledForUser() {
        var userData = service.getUserDataLocked(USER_ID)
        assertThat(userData.supervisionLockScreenEnabled).isFalse()
        assertThat(userData.supervisionLockScreenOptions).isNull()
@@ -87,4 +149,10 @@ class SupervisionServiceTest {
        assertThat(userData.supervisionLockScreenEnabled).isFalse()
        assertThat(userData.supervisionLockScreenOptions).isNull()
    }

    private fun newTargetUser(userId: Int, preCreated: Boolean = false): TargetUser {
        val userInfo = UserInfo(userId, /* name= */ "tempUser", /* flags= */ 0)
        userInfo.preCreated = preCreated
        return TargetUser(userInfo)
    }
}