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

Commit 6a5298ff authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Support GetMetadata for Preference Service" into main

parents dc30635b 8927e437
Loading
Loading
Loading
Loading
+30 −8
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.settings.service

import android.app.Application
import android.os.Binder
import android.os.OutcomeReceiver
import android.os.Process
@@ -26,38 +27,49 @@ import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceService
import com.android.settingslib.graph.GetPreferenceGraphApiHandler
import com.android.settingslib.graph.GetPreferenceGraphRequest
import com.android.settingslib.graph.PreferenceGetterApiHandler
import com.android.settingslib.graph.PreferenceGetterFlags
import com.android.settingslib.graph.PreferenceSetterApiHandler
import com.android.settingslib.ipc.ApiPermissionChecker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import java.lang.Exception

class PreferenceService : SettingsPreferenceService() {

    private val scope = CoroutineScope(Job() + Dispatchers.Main)
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

    private val getApiHandler = PreferenceGetterApiHandler(1, ApiPermissionChecker.alwaysAllow())
    private val setApiHandler = PreferenceSetterApiHandler(2, ApiPermissionChecker.alwaysAllow())
    private val graphApi = GraphProvider(3)

    override fun onGetAllPreferenceMetadata(
        request: MetadataRequest,
        callback: OutcomeReceiver<MetadataResult, Exception>
    ) {
        // TODO(379750656): Update graph API to be usable outside SettingsLib
        callback.onError(UnsupportedOperationException("Not yet supported"))
        scope.launch {
            val graphProto = graphApi.invoke(application, Process.myUid(), Binder.getCallingUid(),
                GetPreferenceGraphRequest(
                    includeValue = false,
                    flags = PreferenceGetterFlags.METADATA
                ))
            val result = transformCatalystGetMetadataResponse(this@PreferenceService, graphProto)
            callback.onResult(result)
        }
    }

    override fun onGetPreferenceValue(
        request: GetValueRequest,
        callback: OutcomeReceiver<GetValueResult, Exception>
    ) {
        scope.launch(Dispatchers.IO) {
        scope.launch {
            val apiRequest = transformFrameworkGetValueRequest(request)
            val response = getApiHandler.invoke(application, Process.myUid(),
                Binder.getCallingPid(), apiRequest)
                Binder.getCallingUid(), apiRequest)
            val result = transformCatalystGetValueResponse(
                this@PreferenceService,
                request,
@@ -75,7 +87,7 @@ class PreferenceService : SettingsPreferenceService() {
        request: SetValueRequest,
        callback: OutcomeReceiver<SetValueResult, Exception>
    ) {
        scope.launch(Dispatchers.IO) {
        scope.launch {
            val apiRequest = transformFrameworkSetValueRequest(request)
            if (apiRequest == null) {
                callback.onResult(
@@ -83,10 +95,20 @@ class PreferenceService : SettingsPreferenceService() {
                )
            } else {
                val response = setApiHandler.invoke(application, Process.myUid(),
                    Binder.getCallingPid(), apiRequest)
                    Binder.getCallingUid(), apiRequest)

                callback.onResult(transformCatalystSetValueResponse(response))
            }
        }
    }

    // Basic implementation - we already have permission to access Graph for Metadata via superclass
    private class GraphProvider(override val id: Int) : GetPreferenceGraphApiHandler(emptySet()) {
        override fun hasPermission(
            application: Application,
            myUid: Int,
            callingUid: Int,
            request: GetPreferenceGraphRequest
        ) = true
    }
}
+52 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settings.service
import android.content.Context
import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult
import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceMetadata
@@ -34,9 +35,55 @@ import com.android.settingslib.graph.preferenceValueProto
import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.graph.getText
import com.android.settingslib.graph.proto.PreferenceGraphProto
import com.android.settingslib.graph.proto.PreferenceOrGroupProto
import com.android.settingslib.graph.toIntent
import com.android.settingslib.metadata.SensitivityLevel

/** Transform Catalyst Graph result to Framework GET METADATA result */
fun transformCatalystGetMetadataResponse(
    context: Context,
    graph: PreferenceGraphProto
): MetadataResult {
    val preferences = mutableSetOf<PreferenceWithScreen>()
    // recursive function to visit all nodes in preference group
    fun traverseGroupOrPref(
        screenKey: String,
        groupOrPref: PreferenceOrGroupProto,
    ) {
        when (groupOrPref.kindCase) {
            PreferenceOrGroupProto.KindCase.PREFERENCE ->
                preferences.add(
                    PreferenceWithScreen(screenKey, groupOrPref.preference)
                )
            PreferenceOrGroupProto.KindCase.GROUP -> {
                for (child in groupOrPref.group.preferencesList) {
                    traverseGroupOrPref(screenKey, child)
                }
            }
            else -> {}
        }
    }
    // traverse all screens and all preferences on screen
    for ((screenKey, screen) in graph.screensMap) {
        for (groupOrPref in screen.root.preferencesList) {
            traverseGroupOrPref(screenKey, groupOrPref)
        }
    }

    return if (preferences.isNotEmpty()) {
        MetadataResult.Builder(MetadataResult.RESULT_OK)
            .setMetadataList(
                preferences.map {
                    it.preference.toMetadata(context, it.screenKey)
                }
            )
            .build()
    } else {
        MetadataResult.Builder(MetadataResult.RESULT_UNSUPPORTED).build()
    }
}

/** Translate Framework GET VALUE request to Catalyst GET VALUE request */
fun transformFrameworkGetValueRequest(
    request: GetValueRequest,
@@ -133,6 +180,11 @@ fun transformCatalystSetValueResponse(@PreferenceSetterResult response: Int): Se
    return SetValueResult.Builder(resultCode).build()
}

private data class PreferenceWithScreen(
    val screenKey: String,
    val preference: PreferenceProto,
)

private fun PreferenceProto.toMetadata(
    context: Context,
    screenKey: String
+74 −1
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.settings.service

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.platform.test.annotations.RequiresFlagsEnabled
@@ -24,6 +23,7 @@ import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult
import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceMetadata
@@ -37,9 +37,15 @@ import com.android.settingslib.graph.PreferenceGetterErrorCode
import com.android.settingslib.graph.PreferenceGetterFlags
import com.android.settingslib.graph.PreferenceGetterResponse
import com.android.settingslib.graph.PreferenceSetterResult
import com.android.settingslib.graph.preferenceGroupProto
import com.android.settingslib.graph.preferenceOrGroupProto
import com.android.settingslib.graph.preferenceProto
import com.android.settingslib.graph.preferenceScreenProto
import com.android.settingslib.graph.proto.PreferenceGraphProto
import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.graph.proto.TextProto
import com.android.settingslib.graph.textProto
import com.android.settingslib.graph.toProto
import com.android.settingslib.metadata.SensitivityLevel
import com.google.common.truth.Truth.assertThat
@@ -54,6 +60,73 @@ class PreferenceServiceRequestTransformerTest {
    @get:Rule
    val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()

    @Test
    fun transformCatalystGetMetadataResponse_emptyGraph_returnsFrameworkResponseWithError() {
        val context: Context = ApplicationProvider.getApplicationContext()
        val graphProto = PreferenceGraphProto.newBuilder().build()
        val fResult = transformCatalystGetMetadataResponse(context, graphProto)
        with(fResult) {
            assertThat(resultCode).isEqualTo(MetadataResult.RESULT_UNSUPPORTED)
            assertThat(metadataList).isEmpty()
        }
    }

    @Test
    fun transformCatalystGetMetadataResponse_populatedGraph_returnsFrameworkResponseWithSuccess() {
        val context: Context = ApplicationProvider.getApplicationContext()
        val screen = preferenceScreenProto {
            root = preferenceGroupProto {
                addAllPreferences(
                    listOf(
                        preferenceOrGroupProto {
                            group = preferenceGroupProto {
                                addPreferences(
                                    preferenceOrGroupProto {
                                        preference = preferenceProto {
                                            key = "key1"
                                            title = textProto { string = "title1" }
                                            enabled = true
                                        }
                                    }
                                )
                            }
                        },
                        preferenceOrGroupProto {
                            preference = preferenceProto {
                                key = "key2"
                                title = textProto { string = "title2" }
                                enabled = false
                            }
                        }
                    )
                )
            }
        }
        val graphProto = PreferenceGraphProto.newBuilder().putScreens("screen", screen).build()

        val fResult = transformCatalystGetMetadataResponse(context, graphProto)
        with(fResult) {
            assertThat(resultCode).isEqualTo(MetadataResult.RESULT_OK)
            assertThat(metadataList.size).isEqualTo(2)
        }
        assertThat(
            fResult.metadataList.any {
                it.key == "key1" &&
                it.screenKey == "screen" &&
                it.title == "title1" &&
                it.isEnabled == true
            }
        ).isTrue()
        assertThat(
            fResult.metadataList.any {
                it.key == "key2" &&
                it.screenKey == "screen" &&
                it.title == "title2" &&
                it.isEnabled == false
            }
        ).isTrue()
    }

    @Test
    fun transformFrameworkGetValueRequest_returnsValidCatalystRequest() {
        val fRequest = GetValueRequest.Builder("screen", "pref").build()