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

Commit c6a5f27c authored by juquan's avatar juquan
Browse files

[Device Supervision] createConfirmSupervisionCredentialsIntent API follow ups

1. Use shared log tag
2. Check caller for ConfirmSupervisionCredentialsActivity

Bug: 403575468
Flag: android.app.supervision.flags.supervision_manager_apis
Test: atest ConfirmSupervisionCredentialsActivityTest
Change-Id: I094721c58fa517feb29e6105d33237769a5ad51e
parent e2984b4b
Loading
Loading
Loading
Loading
+42 −13
Original line number Diff line number Diff line
@@ -17,17 +17,23 @@ package com.android.settings.supervision

import android.Manifest.permission.USE_BIOMETRIC
import android.app.Activity
import android.app.role.RoleManager
import android.content.pm.PackageManager
import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.BiometricPrompt
import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback
import android.os.Binder
import android.os.Bundle
import android.os.CancellationSignal
import android.os.Process
import android.util.Log
import androidx.annotation.OpenForTesting
import androidx.annotation.RequiresPermission
import androidx.annotation.VisibleForTesting
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import com.android.settings.R
import com.android.settingslib.supervision.SupervisionLog

/**
 * Activity for confirming supervision credentials using device credential authentication.
@@ -46,11 +52,15 @@ import com.android.settings.R
 * Permissions:
 * - Requires `android.permission.USE_BIOMETRIC`.
 */
class ConfirmSupervisionCredentialsActivity : FragmentActivity() {
@OpenForTesting
open class ConfirmSupervisionCredentialsActivity : FragmentActivity() {
    private val mAuthenticationCallback =
        object : AuthenticationCallback() {
            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                Log.w(TAG, "onAuthenticationError(errorCode=$errorCode, errString=$errString)")
                Log.w(
                    SupervisionLog.TAG,
                    "onAuthenticationError(errorCode=$errorCode, errString=$errString)",
                )
                setResult(Activity.RESULT_CANCELED)
                finish()
            }
@@ -67,19 +77,24 @@ class ConfirmSupervisionCredentialsActivity : FragmentActivity() {
        }

    @RequiresPermission(USE_BIOMETRIC)
    override fun onCreate(savedInstanceState: Bundle?) {
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // TODO(b/392961554): Check if caller is the SYSTEM_SUPERVISION role holder. Call
        // RoleManager#getRoleHolders(SYSTEM_SUPERVISION) and check if getCallingPackage() is in the
        // list.
        if (checkCallingOrSelfPermission(USE_BIOMETRIC) == PackageManager.PERMISSION_GRANTED) {
            showBiometricPrompt()
        // TODO(b/392961554): adapts to new user profile type to trigger PIN verification dialog.
        if (!callerHasSupervisionRole() && !callerIsSystemUid()) {
            setResult(Activity.RESULT_CANCELED)
            finish()
            return
        }
        if (checkCallingOrSelfPermission(USE_BIOMETRIC) != PackageManager.PERMISSION_GRANTED) {
            setResult(Activity.RESULT_CANCELED)
            finish()
            return
        }
        showBiometricPrompt()
    }

    @RequiresPermission(USE_BIOMETRIC)
    fun showBiometricPrompt() {
        // TODO(b/392961554): adapts to new user profile type to trigger PIN verification dialog.
        val biometricPrompt =
            BiometricPrompt.Builder(this)
                .setTitle(getString(R.string.supervision_full_screen_pin_verification_title))
@@ -93,9 +108,23 @@ class ConfirmSupervisionCredentialsActivity : FragmentActivity() {
        )
    }

    companion object {
        // TODO(b/392961554): remove this tag and use shared tag after http://ag/31997167 is
        // submitted.
        const val TAG = "SupervisionSettings"
    private fun callerHasSupervisionRole(): Boolean {
        val roleManager = getSystemService(RoleManager::class.java)
        if (roleManager == null) {
            Log.w(SupervisionLog.TAG, "null RoleManager")
            return false
        }
        return roleManager
            .getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION)
            .contains(callingPackage)
    }

    private fun callerIsSystemUid(): Boolean {
        val callingUid = Binder.getCallingUid()
        if (callingUid != Process.SYSTEM_UID) {
            Log.w(SupervisionLog.TAG, "callingUid: $callingUid is not SYSTEM_UID")
            return false
        }
        return true
    }
}
+112 −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.Activity
import android.app.role.RoleManager
import android.content.pm.PackageManager
import android.os.Build
import android.os.Process
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.shadows.ShadowBinder

@RunWith(RobolectricTestRunner::class)
class ConfirmSupervisionCredentialsActivityTest {
    private val mockRoleManager = mock<RoleManager>()

    private lateinit var mActivity: ConfirmSupervisionCredentialsActivity

    private val callingPackage = "com.example.caller"

    @Before
    fun setUp() {
        mActivity =
            spy(
                Robolectric.buildActivity(ConfirmSupervisionCredentialsActivity::class.java).get()
            ) {
                on { getSystemService(RoleManager::class.java) } doReturn mockRoleManager
                on { callingPackage } doReturn callingPackage
            }
    }

    @Test
    fun onCreate_noRequiredPermission_finish() {
        whenever(mActivity.checkCallingOrSelfPermission(any())).thenReturn(PackageManager.PERMISSION_DENIED)

        mActivity.onCreate(null)

        verify(mActivity, times(1)).setResult(Activity.RESULT_CANCELED)
        verify(mActivity, times(1)).finish()
    }

    @Test
    fun onCreate_callerHasSupervisionRole_doesNotFinish() {
        whenever(mActivity.checkCallingOrSelfPermission(any())).thenReturn(PackageManager.PERMISSION_GRANTED)
        whenever(mockRoleManager.getRoleHolders(any())).thenReturn(listOf(callingPackage))

        mActivity.onCreate(null)

        verify(mActivity, never()).finish()
    }

    @Test
    fun onCreate_callerNotHasSupervisionRole_finish() {
        val otherPackage = "com.example.other"
        whenever(mockRoleManager.getRoleHolders(any())).thenReturn(listOf(otherPackage))
        whenever(mActivity.checkCallingOrSelfPermission(any())).thenReturn(PackageManager.PERMISSION_GRANTED)

        mActivity.onCreate(null)

        verify(mActivity, times(1)).setResult(Activity.RESULT_CANCELED)
        verify(mActivity, times(1)).finish()
    }

    @Test
    @Config(sdk = [Build.VERSION_CODES.BAKLAVA])
    fun onCreate_callerIsSystemUid_doesNotFinish() {
        whenever(mActivity.checkCallingOrSelfPermission(any())).thenReturn(PackageManager.PERMISSION_GRANTED)
        ShadowBinder.setCallingUid(Process.SYSTEM_UID)

        mActivity.onCreate(null)

        verify(mActivity, never()).finish()
    }

    @Test
    @Config(sdk = [Build.VERSION_CODES.BAKLAVA])
    fun onCreate_callerIsUnknownUid_finish() {
        whenever(mActivity.checkCallingOrSelfPermission(any())).thenReturn(PackageManager.PERMISSION_GRANTED)
        ShadowBinder.setCallingUid(Process.NOBODY_UID)

        mActivity.onCreate(null)

        verify(mActivity, times(1)).setResult(Activity.RESULT_CANCELED)
        verify(mActivity, times(1)).finish()
    }
}