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

Commit 25c6d6b3 authored by Rafael Prado's avatar Rafael Prado
Browse files

Implement the `EnableSupervisionActivity` for enabling supervision.

This activity allows the system supervision role holder to enable supervision on the device. It will respond to `ENABLE_SUPERVISION` intent.

Bug: 417449994
Flag: android.app.supervision.flags.supervision_manager_apis
Test: atest EnableSupervisionActivityTest

Change-Id: I2de278b7b8cbcaf832342dba036883787e40187a
parent d5aba039
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -2834,6 +2834,17 @@
            </intent-filter>
        </activity>

        <activity android:name=".supervision.EnableSupervisionActivity"
            android:excludeFromRecents="true"
            android:theme="@style/Transparent"
            android:exported="true"
            android:featureFlag="android.app.supervision.flags.supervision_manager_apis">
            <intent-filter>
                <action android:name="android.app.supervision.action.ENABLE_SUPERVISION" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

        <activity android:name=".supervision.SetupSupervisionActivity"
            android:excludeFromRecents="true"
            android:exported="false" />
+60 −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.supervision.SupervisionManager
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.FragmentActivity
import com.android.settingslib.supervision.SupervisionLog.TAG

/**
 * Activity for enabling device supervision.
 *
 * This activity is only available to the system supervision role and allowlisted packages.
 * It enables device supervision and finishes the activity with `Activity.RESULT_OK`.
 */
class EnableSupervisionActivity : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // TODO(b/418754198): Check allowlist of packages that can call this activity.
        if (!isCallerSystemSupervisionRoleHolder()) {
            Log.w(TAG, "Caller is not the system supervision role holder. Finishing activity.")
            setResult(RESULT_CANCELED)
            finish()
            return
        }

        val supervisionManager = getSystemService(SupervisionManager::class.java)
        if (supervisionManager == null) {
            Log.e(TAG, "SupervisionManager is null. Finishing activity.")
            setResult(RESULT_CANCELED)
            finish()
            return
        }

        // TODO(b/418754390): Grant the regular SUPERVISION role to the requester.

        supervisionManager.setSupervisionEnabled(true)
        setResult(RESULT_OK)
        finish()
    }

    private fun isCallerSystemSupervisionRoleHolder(): Boolean {
        return callingPackage == supervisionPackageName
    }
}
+89 −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.ROLE_SYSTEM_SUPERVISION
import android.app.supervision.SupervisionManager
import android.content.Context
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.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions
import org.robolectric.Robolectric
import org.robolectric.Shadows.shadowOf
import org.robolectric.android.controller.ActivityController
import org.robolectric.shadow.api.Shadow
import org.robolectric.shadows.ShadowActivity
import org.robolectric.shadows.ShadowContextImpl
import org.robolectric.shadows.ShadowRoleManager

@RunWith(AndroidJUnit4::class)
class EnableSupervisionActivityTest {
    private val mockSupervisionManager = mock<SupervisionManager>()
    private val context = ApplicationProvider.getApplicationContext<Context>()
    private val currentUser = context.user

    private lateinit var mActivity: EnableSupervisionActivity
    private lateinit var mActivityController: ActivityController<EnableSupervisionActivity>
    private lateinit var shadowActivity: ShadowActivity

    private val callingPackage = "com.example.caller"

    @Before
    fun setUp() {
        ShadowRoleManager.reset()

        // Note, we have to use ActivityController (instead of ActivityScenario) in order to access
        // the activity before it is created, so we can set up various mocked responses before they
        // are referenced in onCreate.
        mActivityController = Robolectric.buildActivity(EnableSupervisionActivity::class.java)
        mActivity = mActivityController.get()

        shadowActivity = shadowOf(mActivity)
        shadowActivity.setCallingPackage(callingPackage)
        Shadow.extract<ShadowContextImpl>(mActivity.baseContext).apply {
            setSystemService(Context.SUPERVISION_SERVICE, mockSupervisionManager)
        }
    }

    @Test
    fun onCreate_callerHasSupervisionRole_EnablesSupervision() {
        ShadowRoleManager.addRoleHolder(ROLE_SYSTEM_SUPERVISION, callingPackage, currentUser)

        mActivityController.setup()

        verify(mockSupervisionManager).setSupervisionEnabled(true)
        assertThat(shadowActivity.resultCode).isEqualTo(Activity.RESULT_OK)
        assertThat(mActivity.isFinishing).isTrue()
    }

    @Test
    fun onCreate_callerWithoutSupervisionRole_doesNotEnableSupervision() {
        ShadowRoleManager.addRoleHolder(ROLE_SYSTEM_SUPERVISION, "com.other.package", currentUser)

        mActivityController.setup()

        verifyNoInteractions(mockSupervisionManager)
        assertThat(shadowActivity.resultCode).isEqualTo(Activity.RESULT_CANCELED)
        assertThat(mActivity.isFinishing).isTrue()
    }
}