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

Commit 20aeff8b authored by Xiaomiao Zhang's avatar Xiaomiao Zhang
Browse files

t Add web content filters action metrics for SafeSites preference. Refine...

t Add web content filters action metrics for SafeSites preference. Refine SupervisionSafeSitesPreference.

Fix: 415312413
Test: atest SupervisionSafeSitesPreferenceTest
Test: atest SupervisionWebContentFiltersScreenTest
Flag: android.app.supervision.flags.enable_web_content_filters_screen
Change-Id: I16c32232d6cfb91ab649dafafa1e116552901e78
parent e4c7a5ea
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -27,13 +27,11 @@ import com.android.settingslib.datastore.SettingsStore
/** Datastore of the safe sites preference. */
@Suppress("UNCHECKED_CAST")
class SupervisionSafeSitesDataStore(
    private val context: Context,
    context: Context,
    private val settingsStore: SettingsStore = SettingsSecureStore.get(context),
) : AbstractKeyedDataObservable<String>(), KeyedObserver<String>, KeyValueStore {

    override fun contains(key: String) =
        key == SupervisionBlockExplicitSitesPreference.KEY ||
            key == SupervisionAllowAllSitesPreference.KEY
    override fun contains(key: String) = settingsStore.contains(BROWSER_CONTENT_FILTERS_ENABLED)

    override fun <T : Any> getValue(key: String, valueType: Class<T>): T? {
        val settingValue = settingsStore.getInt(BROWSER_CONTENT_FILTERS_ENABLED)
+19 −26
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
package com.android.settings.supervision

import android.app.Activity
import android.app.settings.SettingsEnums.ACTION_SUPERVISION_ALLOW_ALL_SITES
import android.app.settings.SettingsEnums.ACTION_SUPERVISION_BLOCK_EXPLICIT_SITES
import android.content.Context
import android.content.Intent
import androidx.activity.result.ActivityResult
@@ -24,15 +26,14 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo
import androidx.annotation.VisibleForTesting
import androidx.preference.Preference
import com.android.settings.R
import com.android.settingslib.datastore.Permissions
import com.android.settings.metrics.PreferenceActionMetricsProvider
import com.android.settingslib.datastore.SettingsSecureStore
import com.android.settingslib.metadata.BooleanValuePreference
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.metadata.SensitivityLevel
import com.android.settingslib.preference.PreferenceBinding
import com.android.settingslib.preference.forEachRecursively
import com.android.settingslib.preference.BooleanValuePreferenceBinding
import com.android.settingslib.supervision.SupervisionIntentProvider
import com.android.settingslib.widget.SelectorWithWidgetPreference

@@ -41,8 +42,9 @@ sealed class SupervisionSafeSitesPreference(
    protected val dataStore: SupervisionSafeSitesDataStore
) :
    BooleanValuePreference,
    BooleanValuePreferenceBinding,
    PreferenceActionMetricsProvider,
    SelectorWithWidgetPreference.OnClickListener,
    PreferenceBinding,
    PreferenceLifecycleProvider {
    private lateinit var lifeCycleContext: PreferenceLifecycleContext

@@ -50,22 +52,15 @@ sealed class SupervisionSafeSitesPreference(

    override fun storage(context: Context) = dataStore

    override fun getReadPermissions(context: Context) = Permissions.EMPTY
    override fun getReadPermissions(context: Context) = SettingsSecureStore.getReadPermissions()

    override fun getWritePermissions(context: Context) = Permissions.EMPTY
    override fun getWritePermissions(context: Context) = SettingsSecureStore.getWritePermissions()

    override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
        ReadWritePermit.ALLOW

    override fun getWritePermit(
        context: Context,
        value: Boolean?,
        callingPid: Int,
        callingUid: Int,
    ) = ReadWritePermit.DISALLOW

    override val sensitivityLevel
        get() = SensitivityLevel.NO_SENSITIVITY
    override fun getWritePermit(context: Context, callingPid: Int, callingUid: Int) =
        ReadWritePermit.DISALLOW

    override fun createWidget(context: Context) = SelectorWithWidgetPreference(context)

@@ -85,21 +80,13 @@ sealed class SupervisionSafeSitesPreference(

    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
        super.bind(preference, metadata)
        (preference as SelectorWithWidgetPreference).also {
            it.isChecked = (dataStore.getBoolean(it.key) == true)
            it.setOnClickListener(this)
        }
        (preference as SelectorWithWidgetPreference).setOnClickListener(this)
    }

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    fun onConfirmCredentials(result: ActivityResult) {
        if (result.resultCode == Activity.RESULT_OK) {
            val preference = lifeCycleContext.findPreference<SelectorWithWidgetPreference>(key)
            preference?.parent?.forEachRecursively {
                if (it is SelectorWithWidgetPreference) {
                    it.isChecked = it.key == key
                }
            }
            lifeCycleContext.requirePreference<SelectorWithWidgetPreference>(key).isChecked = true
        }
    }
}
@@ -116,6 +103,9 @@ class SupervisionBlockExplicitSitesPreference(dataStore: SupervisionSafeSitesDat
    override val summary
        get() = R.string.supervision_web_content_filters_browser_block_explicit_sites_summary

    override val preferenceActionMetrics: Int
        get() = ACTION_SUPERVISION_BLOCK_EXPLICIT_SITES

    companion object {
        const val KEY = "web_content_filters_browser_block_explicit_sites"
    }
@@ -130,6 +120,9 @@ class SupervisionAllowAllSitesPreference(dataStore: SupervisionSafeSitesDataStor
    override val title
        get() = R.string.supervision_web_content_filters_browser_allow_all_sites_title

    override val preferenceActionMetrics: Int
        get() = ACTION_SUPERVISION_ALLOW_ALL_SITES

    companion object {
        const val KEY = "web_content_filters_browser_allow_all_sites"
    }
+54 −95
Original line number Diff line number Diff line
@@ -20,9 +20,7 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.provider.Settings
import android.provider.Settings.Secure.BROWSER_CONTENT_FILTERS_ENABLED
import android.provider.Settings.SettingNotFoundException
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
@@ -30,46 +28,49 @@ import androidx.preference.Preference
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.testutils.MetricsRule
import com.android.settingslib.datastore.SettingsSecureStore
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.preference.createAndBindWidget
import com.android.settingslib.widget.SelectorWithWidgetPreference
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions

@RunWith(AndroidJUnit4::class)
class SupervisionSafeSitesPreferenceTest {
    @get:Rule val metricsRule = MetricsRule()

    private val context: Context = ApplicationProvider.getApplicationContext()
    private val dataStore = SupervisionSafeSitesDataStore(context)
    private val allowAllSitesPreference = SupervisionAllowAllSitesPreference(dataStore)
    private val blockExplicitSitesPreference = SupervisionBlockExplicitSitesPreference(dataStore)

    private lateinit var mockLifeCycleContext: PreferenceLifecycleContext
    private lateinit var mockActivityResultLauncher: ActivityResultLauncher<Intent>
    private lateinit var mockPackageManager: PackageManager
    private lateinit var dataStore: SupervisionSafeSitesDataStore
    private lateinit var allowAllSitesPreference: SupervisionAllowAllSitesPreference
    private lateinit var blockExplicitSitesPreference: SupervisionBlockExplicitSitesPreference
    private val mockActivityResultLauncher: ActivityResultLauncher<Intent> = mock()
    private val mockPackageManager: PackageManager = mock {
        on { queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()) } doReturn
            listOf(ResolveInfo())
    }
    private val mockLifeCycleContext: PreferenceLifecycleContext = mock {
        on { packageManager } doReturn mockPackageManager
        on { registerForActivityResult(any<StartActivityForResult>(), any()) } doReturn
            mockActivityResultLauncher
    }

    @Before
    fun setUp() {
        dataStore = SupervisionSafeSitesDataStore(context)
        mockLifeCycleContext = mock(PreferenceLifecycleContext::class.java)
        mockActivityResultLauncher =
            mock(ActivityResultLauncher::class.java) as ActivityResultLauncher<Intent>
        mockPackageManager = mock(PackageManager::class.java)
        mockConfirmSupervisionCredentialsActivity()
        allowAllSitesPreference = SupervisionAllowAllSitesPreference(dataStore)
        allowAllSitesPreference.onCreate(mockLifeCycleContext)
        blockExplicitSitesPreference = SupervisionBlockExplicitSitesPreference(dataStore)
        blockExplicitSitesPreference.onCreate(mockLifeCycleContext)
    }

@@ -95,24 +96,22 @@ class SupervisionSafeSitesPreferenceTest {

    @Test
    fun allowAllSitesIsChecked_whenNoValueIsSet() {
        assertThrows(SettingNotFoundException::class.java) {
            Settings.Secure.getInt(context.getContentResolver(), BROWSER_CONTENT_FILTERS_ENABLED)
        }
        assertThat(getBlockExplicitSitesWidget().isChecked).isFalse()
        assertThat(getAllowAllSitesWidget().isChecked).isTrue()
        setDataStoreValue(null)
        assertThat(blockExplicitSitesPreference.createWidget().isChecked).isFalse()
        assertThat(allowAllSitesPreference.createWidget().isChecked).isTrue()
    }

    @Test
    fun blockExplicitSitesIsChecked_whenPreviouslyEnabled() {
        Settings.Secure.putInt(context.getContentResolver(), BROWSER_CONTENT_FILTERS_ENABLED, 1)
        assertThat(getAllowAllSitesWidget().isChecked).isFalse()
        assertThat(getBlockExplicitSitesWidget().isChecked).isTrue()
        setDataStoreValue(1)
        assertThat(allowAllSitesPreference.createWidget().isChecked).isFalse()
        assertThat(blockExplicitSitesPreference.createWidget().isChecked).isTrue()
    }

    @Test
    fun clickBlockExplicitSites_credentialFailed() {
        Settings.Secure.putInt(context.getContentResolver(), BROWSER_CONTENT_FILTERS_ENABLED, 0)
        val blockExplicitSitesWidget = getBlockExplicitSitesWidget()
        setDataStoreValue(0)
        val blockExplicitSitesWidget = blockExplicitSitesPreference.createWidget()
        assertThat(blockExplicitSitesWidget.isChecked).isFalse()

        blockExplicitSitesWidget.performClick()
@@ -122,42 +121,33 @@ class SupervisionSafeSitesPreferenceTest {
            ActivityResult(Activity.RESULT_CANCELED, null)
        )

        verifyNoInteractions(metricsRule.metricsFeatureProvider)
        assertThat(blockExplicitSitesWidget.isChecked).isFalse()
        assertThat(
                Settings.Secure.getInt(
                    context.getContentResolver(),
                    BROWSER_CONTENT_FILTERS_ENABLED,
                )
            )
            .isEqualTo(0)
        assertThat(getDataStoreValue()).isFalse()
    }

    @Test
    fun clickBlockExplicitSites_unresolvedIntent_activityNotLaunched() {
        `when`(mockPackageManager.queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()))
            .thenReturn(emptyList<ResolveInfo>())
        mockPackageManager.stub {
            on { queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()) } doReturn listOf()
        }

        Settings.Secure.putInt(context.getContentResolver(), BROWSER_CONTENT_FILTERS_ENABLED, 0)
        val blockExplicitSitesWidget = getBlockExplicitSitesWidget()
        setDataStoreValue(0)
        val blockExplicitSitesWidget = blockExplicitSitesPreference.createWidget()
        assertThat(blockExplicitSitesWidget.isChecked).isFalse()

        blockExplicitSitesWidget.performClick()

        verify(mockActivityResultLauncher, never()).launch(any())
        assertThat(
                Settings.Secure.getInt(
                    context.getContentResolver(),
                    BROWSER_CONTENT_FILTERS_ENABLED,
                )
            )
            .isEqualTo(0)
        verifyNoInteractions(metricsRule.metricsFeatureProvider)
        assertThat(getDataStoreValue()).isFalse()
        assertThat(blockExplicitSitesWidget.isChecked).isFalse()
    }

    @Test
    fun clickBlockExplicitSites_enablesFilter() {
        Settings.Secure.putInt(context.getContentResolver(), BROWSER_CONTENT_FILTERS_ENABLED, -1)
        val blockExplicitSitesWidget = getBlockExplicitSitesWidget()
        setDataStoreValue(-1)
        val blockExplicitSitesWidget = blockExplicitSitesPreference.createWidget()
        assertThat(blockExplicitSitesWidget.isChecked).isFalse()

        blockExplicitSitesWidget.performClick()
@@ -166,19 +156,14 @@ class SupervisionSafeSitesPreferenceTest {
        blockExplicitSitesPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null))

        assertThat(blockExplicitSitesWidget.isChecked).isTrue()
        assertThat(
                Settings.Secure.getInt(
                    context.getContentResolver(),
                    BROWSER_CONTENT_FILTERS_ENABLED,
                )
            )
            .isEqualTo(1)
        assertThat(getDataStoreValue()).isTrue()
        verify(metricsRule.metricsFeatureProvider).changed(0, blockExplicitSitesPreference.key, 1)
    }

    @Test
    fun clickAllowAllSites_disablesFilter() {
        Settings.Secure.putInt(context.getContentResolver(), BROWSER_CONTENT_FILTERS_ENABLED, 1)
        val allowAllSitesWidget = getAllowAllSitesWidget()
        setDataStoreValue(1)
        val allowAllSitesWidget = allowAllSitesPreference.createWidget()
        assertThat(allowAllSitesWidget.isChecked).isFalse()
        allowAllSitesWidget.performClick()

@@ -186,46 +171,20 @@ class SupervisionSafeSitesPreferenceTest {
        allowAllSitesPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null))

        assertThat(allowAllSitesWidget.isChecked).isTrue()
        assertThat(
                Settings.Secure.getInt(
                    context.getContentResolver(),
                    BROWSER_CONTENT_FILTERS_ENABLED,
                )
            )
            .isEqualTo(0)
        assertThat(getDataStoreValue()).isFalse()
        verify(metricsRule.metricsFeatureProvider).changed(0, allowAllSitesPreference.key, 1)
    }

    private fun getBlockExplicitSitesWidget(): SelectorWithWidgetPreference {
        val widget: SelectorWithWidgetPreference =
            blockExplicitSitesPreference.createAndBindWidget(context)
        mockLifeCycleContext.stub {
            on { findPreference<Preference>(SupervisionBlockExplicitSitesPreference.KEY) } doReturn
                widget
            on {
                requirePreference<Preference>(SupervisionBlockExplicitSitesPreference.KEY)
            } doReturn widget
        }
        return widget
    }
    private fun getDataStoreValue() =
        SettingsSecureStore.get(context).getBoolean(BROWSER_CONTENT_FILTERS_ENABLED)

    private fun getAllowAllSitesWidget(): SelectorWithWidgetPreference {
        val widget: SelectorWithWidgetPreference =
            allowAllSitesPreference.createAndBindWidget(context)
        mockLifeCycleContext.stub {
            on { findPreference<Preference>(SupervisionAllowAllSitesPreference.KEY) } doReturn
                widget
            on { requirePreference<Preference>(SupervisionAllowAllSitesPreference.KEY) } doReturn
                widget
        }
        return widget
    private fun setDataStoreValue(value: Int?) {
        SettingsSecureStore.get(context).setInt(BROWSER_CONTENT_FILTERS_ENABLED, value)
    }

    private fun mockConfirmSupervisionCredentialsActivity() {
        `when`(mockPackageManager.queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()))
            .thenReturn(listOf(ResolveInfo()))
        `when`(mockLifeCycleContext.packageManager).thenReturn(mockPackageManager)
        `when`(mockLifeCycleContext.registerForActivityResult(any<StartActivityForResult>(), any()))
            .thenReturn(mockActivityResultLauncher)
    private fun SupervisionSafeSitesPreference.createWidget() =
        createAndBindWidget<SelectorWithWidgetPreference>(context).also { widget ->
            mockLifeCycleContext.stub { on { requirePreference<Preference>(key) } doReturn widget }
        }

    private fun verifyConfirmSupervisionCredentialsActivity() {
+3 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.text.Spanned
import android.text.style.ClickableSpan
import android.view.View
import android.widget.TextView
import androidx.fragment.app.testing.FragmentScenario
import androidx.preference.PreferenceGroupAdapter
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -137,6 +138,7 @@ class SupervisionWebContentFiltersScreenTest {
                Activity.RESULT_OK,
                null,
            )
            ShadowLooper.runUiThreadTasksIncludingDelayedTasks()

            assertThat(blockExplicitSitesPreference.isChecked).isTrue()
            assertThat(allowAllSitesPreference.isChecked).isFalse()
@@ -168,6 +170,7 @@ class SupervisionWebContentFiltersScreenTest {
                Activity.RESULT_CANCELED,
                null,
            )
            ShadowLooper.runUiThreadTasksIncludingDelayedTasks()

            assertThat(blockExplicitSitesPreference.isChecked).isFalse()
            assertThat(allowAllSitesPreference.isChecked).isTrue()