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

Commit ecd62678 authored by Zhou Liu's avatar Zhou Liu
Browse files

Clean up the routing logic in TopLevelSupervisionPreferenceController

The routing logic will be reworked in SupervisionDashboardScreen

Bug: 414865683
Test: atest TopLevelSupervisionPreferenceControllerTest
Flag: android.app.supervision.flags.enable_supervision_settings_screen
Change-Id: I189bd10819cac2b7108e1bf1a0dd3323e158f973
parent ffd9e69a
Loading
Loading
Loading
Loading
+2 −58
Original line number Diff line number Diff line
@@ -19,82 +19,26 @@ import android.app.supervision.flags.Flags
import android.content.Context
import android.content.Intent
import androidx.preference.Preference
import com.android.settings.applications.AppStoreUtil.getAppStoreLink
import com.android.settings.applications.AppStoreUtil.getInstallerPackageName
import com.android.settings.core.BasePreferenceController
import com.android.settings.supervision.ipc.SupervisionMessengerClient.Companion.SUPERVISION_MESSENGER_SERVICE_BIND_ACTION

/** Controller for the top level Supervision settings Preference item. */
class TopLevelSupervisionPreferenceController(context: Context, key: String) :
    BasePreferenceController(context, key) {
    private val supervisionPackage = context.supervisionPackageName

    private var missingAppStoreLink = false

    private var redirectIntent: Intent? = null

    override fun handlePreferenceTreeClick(preference: Preference): Boolean {
        if (preference.key == preferenceKey) {
            val intent =
                redirectIntent ?: Intent(mContext, SupervisionDashboardActivity::class.java)
            val intent = Intent(mContext, SupervisionDashboardActivity::class.java)
            mContext.startActivity(intent)
            return true
        }
        return super.handlePreferenceTreeClick(preference)
    }

    override fun updateState(preference: Preference) {
        super.updateState(preference)
        if (!hasNecessarySupervisionComponent() && missingAppStoreLink) {
            preference.isEnabled = false
        }
    }

    override fun getAvailabilityStatus(): Int {
        if (!Flags.enableSupervisionSettingsScreen() || supervisionPackage == null)
        if (!Flags.enableSupervisionSettingsScreen()) {
            return UNSUPPORTED_ON_DEVICE

        // Try to navigate to app store if supervision app with necessary component is not installed
        if (!hasNecessarySupervisionComponent()) {
            val installerPackageName = getInstallerPackageName(mContext, supervisionPackage)
            val appStoreLinkIntent =
                installerPackageName?.let {
                    getAppStoreLink(mContext, installerPackageName, supervisionPackage)
                }
            if (appStoreLinkIntent == null) {
                missingAppStoreLink = true
                return AVAILABLE
            }
            missingAppStoreLink = false
            redirectIntent = appStoreLinkIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }

        if (hasRedirect()) {
            redirectIntent = Intent(SETTINGS_REDIRECT_ACTION).setPackage(supervisionPackage)
        }

        return AVAILABLE
    }

    private fun hasNecessarySupervisionComponent(): Boolean {
        val intent =
            Intent(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION).setPackage(supervisionPackage)

        return supervisionPackage != null &&
            mContext.packageManager.queryIntentServices(intent, 0).isNotEmpty()
    }

    private fun hasRedirect(): Boolean {
        val intent = Intent(SETTINGS_REDIRECT_ACTION).setPackage(supervisionPackage)
        return supervisionPackage != null &&
            mContext.packageManager
                .queryIntentActivitiesAsUser(intent, 0, mContext.userId)
                .isNotEmpty()
    }

    companion object {
        // Supervision app should declare an intent-filter with this action to redirect the settings
        // navigation target.
        const val SETTINGS_REDIRECT_ACTION = "android.app.supervision.action.VIEW_SETTINGS"
    }
}
+2 −123
Original line number Diff line number Diff line
@@ -16,39 +16,23 @@
package com.android.settings.supervision

import android.app.Activity
import android.app.role.RoleManager
import android.content.Context
import android.content.Intent
import android.content.pm.InstallSourceInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import androidx.preference.Preference
import com.android.settings.core.BasePreferenceController.AVAILABLE
import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE
import com.android.settings.supervision.TopLevelSupervisionPreferenceController.Companion.SETTINGS_REDIRECT_ACTION
import com.android.settings.supervision.ipc.SupervisionMessengerClient.Companion.SUPERVISION_MESSENGER_SERVICE_BIND_ACTION
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.argThat
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class TopLevelSupervisionPreferenceControllerTest {
    private val mockRoleManager = mock<RoleManager>()
    private val mockPackageManager = mock<PackageManager>()
    private val context =
        spy(Robolectric.buildActivity(Activity::class.java).get()) {
            on { getSystemService(Context.ROLE_SERVICE) }.thenReturn(mockRoleManager)
            on { packageManager }.thenReturn(mockPackageManager)
        }
        spy(Robolectric.buildActivity(Activity::class.java).get())

    private val preference = Preference(context)

@@ -58,109 +42,7 @@ class TopLevelSupervisionPreferenceControllerTest {
    }

    @Test
    fun supervisionPackageNameIsNull_returnUnsupported() {
        mockRoleManager.stub {
            on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) }.thenReturn(listOf<String>())
        }

        val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY)
        verify(mockRoleManager).getRoleHolders(any())
        assertThat(preferenceController.availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE)
    }

    @Test
    fun noNecessaryComponent_noAppStoreLink_preferenceDisabled() {
        mockRoleManager.stub {
            on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) }
                .thenReturn(listOf(SUPERVISION_PACKAGE_NAME))
        }

        mockPackageManager.stub {
            on {
                    queryIntentServices(
                        actionIntentMatcher(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION),
                        any<Int>(),
                    )
                }
                .thenReturn(listOf())
        }
        mockPackageManager.stub {
            on { getInstallSourceInfo(any()) }.thenReturn(InstallSourceInfo(null, null, null, null))
        }

        val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY)

        assertThat(preferenceController.availabilityStatus).isEqualTo(AVAILABLE)
        preferenceController.handlePreferenceTreeClick(preference)
        preferenceController.updateState(preference)

        assertThat(preference.isEnabled).isFalse()
        verify(context)
            .startActivity(componentIntentMatcher(SupervisionDashboardActivity::class.java))
    }

    @Test
    fun hasNecessaryComponent_isFullySupervised_launchFullSupervision() {
        mockRoleManager.stub {
            on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) }
                .thenReturn(listOf(SUPERVISION_PACKAGE_NAME))
        }

        mockPackageManager.stub {
            on {
                    queryIntentServices(
                        actionIntentMatcher(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION),
                        any<Int>(),
                    )
                }
                .thenReturn(listOf(ResolveInfo()))
        }
        mockPackageManager.stub {
            on {
                    queryIntentActivitiesAsUser(
                        actionIntentMatcher(SETTINGS_REDIRECT_ACTION),
                        any<Int>(),
                        any<Int>(),
                    )
                }
                .thenReturn(listOf(ResolveInfo()))
        }

        val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY)

        assertThat(preferenceController.availabilityStatus).isEqualTo(AVAILABLE)
        preferenceController.handlePreferenceTreeClick(preference)

        verify(context).startActivity(actionIntentMatcher(SETTINGS_REDIRECT_ACTION))
    }

    @Test
    fun hasNecessaryComponent_isNotFullySupervised_returnAvailable() {
        mockRoleManager.stub {
            on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) }
                .thenReturn(listOf(SUPERVISION_PACKAGE_NAME))
        }

        mockPackageManager.stub {
            on {
                    queryIntentServices(
                        actionIntentMatcher(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION),
                        any<Int>(),
                    )
                }
                .thenReturn(listOf(ResolveInfo()))
        }
        mockPackageManager.stub {
            on {
                    queryIntentActivitiesAsUser(
                        actionIntentMatcher(SETTINGS_REDIRECT_ACTION),
                        any<Int>(),
                        any<Int>(),
                    )
                }
                .thenReturn(listOf())
        }

    fun navigateToDashboard() {
        val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY)

        assertThat(preferenceController.availabilityStatus).isEqualTo(AVAILABLE)
@@ -170,13 +52,10 @@ class TopLevelSupervisionPreferenceControllerTest {
            .startActivity(componentIntentMatcher(SupervisionDashboardActivity::class.java))
    }

    private fun actionIntentMatcher(action: String) = argThat<Intent> { this.action == action }

    private fun componentIntentMatcher(cls: Class<*>) =
        argThat<Intent> { this.component?.className == cls.name }

    private companion object {
        const val SUPERVISION_PACKAGE_NAME = "com.android.supervision"
        const val PREFERENCE_KEY = "test_key"
    }
}