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

Commit ff2ab485 authored by Yvonne Jiang's avatar Yvonne Jiang
Browse files

Update createConfirmSupervisionCredentialsIntent to return null if the...

Update createConfirmSupervisionCredentialsIntent to return null if the supervising user is not present or does not have secure credentials.

Bug: 392961554
Flag: android.app.supervision.flags.supervision_manager_apis
Test: atest SupervisionServiceTest
Change-Id: Ide3648dc5dc33c57f9093b450d5f762e7f6517d5
parent 089efb6b
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.pm;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SpecialUsers.CanBeNULL;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.LauncherUserInfo;
@@ -620,11 +621,17 @@ public abstract class UserManagerInternal {
     * Returns the user id of the communal profile, or {@link android.os.UserHandle#USER_NULL}
     * if there is no such user.
     */
    public abstract @UserIdInt int getCommunalProfileId();
    public abstract @CanBeNULL @UserIdInt int getCommunalProfileId();

    /**
     * Checks whether to show a notification for sounds (e.g., alarms, timers, etc.) from
     * background users.
     * Returns the user id of the supervising profile, or {@link android.os.UserHandle#USER_NULL} if
     * there is no such user.
     */
    public abstract @CanBeNULL @UserIdInt int getSupervisingProfileId();

    /**
     * Checks whether to show a notification for sounds (e.g., alarms, timers, etc.) from background
     * users.
     */
    public static boolean shouldShowNotificationForBackgroundUserSounds() {
        return Flags.addUiForSoundsFromBackgroundUsers() && Resources.getSystem().getBoolean(
+19 −1
Original line number Diff line number Diff line
@@ -1521,6 +1521,20 @@ public class UserManagerService extends IUserManager.Stub {
        return UserHandle.USER_NULL;
    }

    /** Returns the currently-designated supervising profile, or USER_NULL if not present. */
    private @CanBeNULL @UserIdInt int getSupervisingProfileId() {
        synchronized (mUsersLock) {
            final int userSize = mUsers.size();
            for (int i = 0; i < userSize; i++) {
                final UserInfo user = mUsers.valueAt(i).info;
                if (user.isSupervisingProfile() && !mRemovingUserIds.get(user.id)) {
                    return user.id;
                }
            }
        }
        return UserHandle.USER_NULL;
    }

    public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
        return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */
                true);
@@ -8335,10 +8349,14 @@ public class UserManagerService extends IUserManager.Stub {
        }

        @Override
        public @UserIdInt int getCommunalProfileId() {
        public @CanBeNULL @UserIdInt int getCommunalProfileId() {
            return getCommunalProfileIdUnchecked();
        }

        @Override
        public @CanBeNULL @UserIdInt int getSupervisingProfileId() {
            return UserManagerService.this.getSupervisingProfileId();
        }
    } // class LocalService


+23 −2
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.supervision.ISupervisionManager;
@@ -147,9 +148,21 @@ public class SupervisionService extends ISupervisionManager.Stub {
    @Override
    @Nullable
    public Intent createConfirmSupervisionCredentialsIntent() {
        // TODO(b/392961554): (1) Return null if supervision is not enabled.
        // (2) check if PIN exists before return a valid intent.
        enforceAnyPermission(QUERY_USERS, MANAGE_USERS);
        if (!isSupervisionEnabledForUser(mContext.getUserId())) {
            return null;
        }
        // Verify the supervising user profile exists and has a secure credential set.
        final int supervisingUserId = mInjector.getUserManagerInternal().getSupervisingProfileId();
        final long token = Binder.clearCallingIdentity();
        try {
            if (supervisingUserId == UserHandle.USER_NULL
                    || !mInjector.getKeyguardManager().isDeviceSecure(supervisingUserId)) {
                return null;
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        final Intent intent = new Intent(ACTION_CONFIRM_SUPERVISION_CREDENTIALS);
        // explicitly set the package for security
        intent.setPackage("com.android.settings");
@@ -277,6 +290,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
    static class Injector {
        private final Context mContext;
        private DevicePolicyManagerInternal mDpmInternal;
        private KeyguardManager mKeyguardManager;
        private PackageManager mPackageManager;
        private UserManagerInternal mUserManagerInternal;

@@ -292,6 +306,13 @@ public class SupervisionService extends ISupervisionManager.Stub {
            return mDpmInternal;
        }

        KeyguardManager getKeyguardManager() {
            if (mKeyguardManager == null) {
                mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
            }
            return mKeyguardManager;
        }

        PackageManager getPackageManager() {
            if (mPackageManager == null) {
                mPackageManager = mContext.getPackageManager();
+47 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.supervision

import android.app.Activity
import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManagerInternal
import android.app.supervision.flags.Flags
@@ -61,6 +62,9 @@ class SupervisionServiceTest {
    @get:Rule val mocks: MockitoRule = MockitoJUnit.rule()

    @Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal

    @Mock
    private lateinit var mockKeyguardManager: KeyguardManager
    @Mock private lateinit var mockPackageManager: PackageManager
    @Mock private lateinit var mockUserManagerInternal: UserManagerInternal

@@ -71,7 +75,7 @@ class SupervisionServiceTest {
    @Before
    fun setUp() {
        context = InstrumentationRegistry.getInstrumentation().context
        context = SupervisionContextWrapper(context, mockPackageManager)
        context = SupervisionContextWrapper(context, mockKeyguardManager, mockPackageManager)

        LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java)
        LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal)
@@ -250,11 +254,41 @@ class SupervisionServiceTest {

    @Test
    fun createConfirmSupervisionCredentialsIntent() {
        service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true)
        whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
        whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true)

        val intent = checkNotNull(service.createConfirmSupervisionCredentialsIntent())
        assertThat(intent.action).isEqualTo(ACTION_CONFIRM_SUPERVISION_CREDENTIALS)
        assertThat(intent.getPackage()).isEqualTo("com.android.settings")
    }

    @Test
    fun createConfirmSupervisionCredentialsIntent_supervisionNotEnabled_returnsNull() {
        service.mInternal.setSupervisionEnabledForUser(context.getUserId(), false)
        whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
        whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true)

        assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
    }

    @Test
    fun createConfirmSupervisionCredentialsIntent_noSupervisingUser_returnsNull() {
        service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true)
        whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(UserHandle.USER_NULL)

        assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
    }

    @Test
    fun createConfirmSupervisionCredentialsIntent_supervisingUserMissingSecureLock_returnsNull() {
        service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true)
        whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
        whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(false)

        assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
    }

    private val systemSupervisionPackage: String
        get() = context.getResources().getString(R.string.config_systemSupervision)

@@ -279,6 +313,7 @@ class SupervisionServiceTest {
    private companion object {
        const val USER_ID = 100
        const val APP_UID = USER_ID * UserHandle.PER_USER_RANGE
        const val SUPERVISING_USER_ID = 10
    }
}

@@ -286,10 +321,19 @@ class SupervisionServiceTest {
 * A context wrapper that allows broadcast intents to immediately invoke the receivers without
 * performing checks on the sending user.
 */
private class SupervisionContextWrapper(val context: Context, val pkgManager: PackageManager) :
    ContextWrapper(context) {
private class SupervisionContextWrapper(
    val context: Context,
    val keyguardManager: KeyguardManager,
    val pkgManager: PackageManager,
) : ContextWrapper(context) {
    val interceptors = mutableListOf<Pair<BroadcastReceiver, IntentFilter>>()

    override fun getSystemService(name: String): Any =
        when (name) {
            Context.KEYGUARD_SERVICE -> keyguardManager
            else -> super.getSystemService(name)
        }

    override fun getPackageManager() = pkgManager

    override fun registerReceiverForAllUsers(