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

Commit efe946d6 authored by Zhou Liu's avatar Zhou Liu Committed by Android (Google) Code Review
Browse files

Merge "[Device Supervision] Get the supervision package name from RoleManager" into main

parents 7adf0e56 758c7296
Loading
Loading
Loading
Loading
+24 −1
Original line number Diff line number Diff line
@@ -16,14 +16,16 @@
package com.android.settings.supervision

import android.app.KeyguardManager
import android.app.role.RoleManager
import android.content.Context
import android.os.UserHandle
import android.os.UserManager
import android.os.UserManager.USER_TYPE_PROFILE_SUPERVISING
import android.util.Log
import androidx.annotation.VisibleForTesting

/** Convenience methods for interacting with the supervising user profile. */
open class SupervisionHelper private constructor(context: Context) {
open class SupervisionHelper private constructor(private val context: Context) {
    private val mUserManager = context.getSystemService(UserManager::class.java)
    private val mKeyguardManager = context.getSystemService(KeyguardManager::class.java)

@@ -41,7 +43,28 @@ open class SupervisionHelper private constructor(context: Context) {
        return mKeyguardManager?.isDeviceSecure(supervisingUserId) ?: false
    }

    /**
     * Retrieves the package name of the system supervision app.
     *
     * @return The package name of the system supervision app, or null if not found.
     */
    fun getSupervisionPackageName(): String? {
        val roleManager = context.getSystemService(RoleManager::class.java)
        if (roleManager == null) {
            Log.w(TAG, "RoleManager service not available.")
            return null
        }

        val roleHolders = roleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION)
        if (roleHolders.isEmpty()) Log.w(TAG, "No package holding the system supervision role.")

        // supervision role is exclusive, only one app may hold this role in a user
        return roleHolders.firstOrNull()
    }

    companion object {
        private const val TAG = "SupervisionSettings"

        @Volatile @VisibleForTesting var sInstance: SupervisionHelper? = null

        fun getInstance(context: Context): SupervisionHelper {
+10 −9
Original line number Diff line number Diff line
@@ -18,9 +18,10 @@ package com.android.settings.supervision.ipc
import android.content.Context
import android.content.Intent
import android.util.Log
import com.android.internal.R
import com.android.settings.supervision.PreferenceDataProvider
import com.android.settings.supervision.SupervisionHelper
import com.android.settingslib.ipc.MessengerServiceClient
import com.android.settingslib.supervision.SupervisionLog

/**
 * A specialized [MessengerServiceClient] for interacting with the system supervision service.
@@ -34,14 +35,13 @@ import com.android.settingslib.ipc.MessengerServiceClient
class SupervisionMessengerClient(context: Context) :
    MessengerServiceClient(context), PreferenceDataProvider {

    // TODO(b/399497788): get the package name from RoleManager and do pre-check before calling the
    //  messenger service
    private val supervisionPackageName: String =
        context.getResources().getString(R.string.config_systemSupervision)

    override val serviceIntentFactory: () -> Intent
        get() = { Intent(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION) }

    private val supervisionPackageName: String? by lazy {
        SupervisionHelper.getInstance(context).getSupervisionPackageName()
    }

    /**
     * Retrieves preference data from the system supervision app.
     *
@@ -55,15 +55,16 @@ class SupervisionMessengerClient(context: Context) :
     */
    override suspend fun getPreferenceData(keys: List<String>): Map<String, PreferenceData> =
        try {
            invoke(supervisionPackageName, PreferenceDataApi(), PreferenceDataRequest(keys = keys))
            val targetPackageName = supervisionPackageName ?: return mapOf()

            invoke(targetPackageName, PreferenceDataApi(), PreferenceDataRequest(keys = keys))
                .await()
        } catch (e: Exception) {
            Log.e(TAG, "Error fetching Preference data from supervision app", e)
            Log.e(SupervisionLog.TAG, "Error fetching Preference data from supervision app", e)
            mapOf()
        }

    companion object {
        private const val TAG = "SupervisionMessengerClient"
        private const val SUPERVISION_MESSENGER_SERVICE_BIND_ACTION =
            "android.app.supervision.action.SUPERVISION_MESSENGER_SERVICE"
    }
+92 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.settings.supervision

import android.app.role.RoleManager
import android.content.Context
import android.content.ContextWrapper
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify

@RunWith(AndroidJUnit4::class)
class SupervisionHelperTest {

    private lateinit var context: Context
    private val mockRoleManager = mock<RoleManager>()
    private lateinit var supervisionHelper: SupervisionHelper

    @Before
    fun setUp() {
        context = contextOfRoleManager(mockRoleManager)
        SupervisionHelper.sInstance = null
    }

    @Test
    fun getSupervisionPackageName_roleManagerReturnsPackageName_shouldReturnPackageName() {
        val testPackageName = "com.android.supervision"
        mockRoleManager.stub {
            on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) } doReturn
                listOf(testPackageName)
        }

        supervisionHelper = SupervisionHelper.getInstance(context)
        val result = supervisionHelper.getSupervisionPackageName()

        assertThat(result).isEqualTo(testPackageName)
        verify(mockRoleManager).getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION)
    }

    @Test
    fun getSupervisionPackageName_roleManagerReturnsEmptyList_shouldReturnNull() {
        mockRoleManager.stub {
            on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) } doReturn emptyList()
        }

        supervisionHelper = SupervisionHelper.getInstance(context)
        val result = supervisionHelper.getSupervisionPackageName()

        assertThat(result).isNull()
        verify(mockRoleManager).getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION)
    }

    @Test
    fun getSupervisionPackageName_roleManagerReturnsNull_shouldReturnNull() {
        context = contextOfRoleManager(null)

        supervisionHelper = SupervisionHelper.getInstance(context)
        val result = supervisionHelper.getSupervisionPackageName()

        assertThat(result).isNull()
    }

    private fun contextOfRoleManager(roleManager: RoleManager?): Context {
        return object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
            override fun getSystemService(name: String): Any? =
                when (name) {
                    ROLE_SERVICE -> roleManager
                    else -> super.getSystemService(name)
                }
        }
    }
}