Support GetMetadata for Preference Service

Bug: 379750656
Flag: com.android.settingslib.flags.settings_catalyst
Test: unit test
Change-Id: Ia9b438360b60ff509a259df0a079ec4d745fb595
This commit is contained in:
Chris Antol
2024-12-11 23:36:12 +00:00
parent 1a4de9370a
commit 8927e4371d
3 changed files with 156 additions and 9 deletions

View File

@@ -16,6 +16,7 @@
package com.android.settings.service package com.android.settings.service
import android.app.Application
import android.os.Binder import android.os.Binder
import android.os.OutcomeReceiver import android.os.OutcomeReceiver
import android.os.Process 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.SetValueRequest
import android.service.settings.preferences.SetValueResult import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceService 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.PreferenceGetterApiHandler
import com.android.settingslib.graph.PreferenceGetterFlags
import com.android.settingslib.graph.PreferenceSetterApiHandler import com.android.settingslib.graph.PreferenceSetterApiHandler
import com.android.settingslib.ipc.ApiPermissionChecker import com.android.settingslib.ipc.ApiPermissionChecker
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.lang.Exception import java.lang.Exception
class PreferenceService : SettingsPreferenceService() { 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 getApiHandler = PreferenceGetterApiHandler(1, ApiPermissionChecker.alwaysAllow())
private val setApiHandler = PreferenceSetterApiHandler(2, ApiPermissionChecker.alwaysAllow()) private val setApiHandler = PreferenceSetterApiHandler(2, ApiPermissionChecker.alwaysAllow())
private val graphApi = GraphProvider(3)
override fun onGetAllPreferenceMetadata( override fun onGetAllPreferenceMetadata(
request: MetadataRequest, request: MetadataRequest,
callback: OutcomeReceiver<MetadataResult, Exception> callback: OutcomeReceiver<MetadataResult, Exception>
) { ) {
// TODO(379750656): Update graph API to be usable outside SettingsLib scope.launch {
callback.onError(UnsupportedOperationException("Not yet supported")) 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( override fun onGetPreferenceValue(
request: GetValueRequest, request: GetValueRequest,
callback: OutcomeReceiver<GetValueResult, Exception> callback: OutcomeReceiver<GetValueResult, Exception>
) { ) {
scope.launch(Dispatchers.IO) { scope.launch {
val apiRequest = transformFrameworkGetValueRequest(request) val apiRequest = transformFrameworkGetValueRequest(request)
val response = getApiHandler.invoke(application, Process.myUid(), val response = getApiHandler.invoke(application, Process.myUid(),
Binder.getCallingPid(), apiRequest) Binder.getCallingUid(), apiRequest)
val result = transformCatalystGetValueResponse( val result = transformCatalystGetValueResponse(
this@PreferenceService, this@PreferenceService,
request, request,
@@ -75,7 +87,7 @@ class PreferenceService : SettingsPreferenceService() {
request: SetValueRequest, request: SetValueRequest,
callback: OutcomeReceiver<SetValueResult, Exception> callback: OutcomeReceiver<SetValueResult, Exception>
) { ) {
scope.launch(Dispatchers.IO) { scope.launch {
val apiRequest = transformFrameworkSetValueRequest(request) val apiRequest = transformFrameworkSetValueRequest(request)
if (apiRequest == null) { if (apiRequest == null) {
callback.onResult( callback.onResult(
@@ -83,10 +95,20 @@ class PreferenceService : SettingsPreferenceService() {
) )
} else { } else {
val response = setApiHandler.invoke(application, Process.myUid(), val response = setApiHandler.invoke(application, Process.myUid(),
Binder.getCallingPid(), apiRequest) Binder.getCallingUid(), apiRequest)
callback.onResult(transformCatalystSetValueResponse(response)) 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
}
} }

View File

@@ -19,6 +19,7 @@ package com.android.settings.service
import android.content.Context import android.content.Context
import android.service.settings.preferences.GetValueRequest import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult import android.service.settings.preferences.GetValueResult
import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceMetadata 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.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceValueProto import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.graph.getText 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.graph.toIntent
import com.android.settingslib.metadata.SensitivityLevel 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 */ /** Translate Framework GET VALUE request to Catalyst GET VALUE request */
fun transformFrameworkGetValueRequest( fun transformFrameworkGetValueRequest(
request: GetValueRequest, request: GetValueRequest,
@@ -133,6 +180,11 @@ fun transformCatalystSetValueResponse(@PreferenceSetterResult response: Int): Se
return SetValueResult.Builder(resultCode).build() return SetValueResult.Builder(resultCode).build()
} }
private data class PreferenceWithScreen(
val screenKey: String,
val preference: PreferenceProto,
)
private fun PreferenceProto.toMetadata( private fun PreferenceProto.toMetadata(
context: Context, context: Context,
screenKey: String screenKey: String

View File

@@ -16,7 +16,6 @@
package com.android.settings.service package com.android.settings.service
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.platform.test.annotations.RequiresFlagsEnabled 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.platform.test.flag.junit.DeviceFlagsValueProvider
import android.service.settings.preferences.GetValueRequest import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult import android.service.settings.preferences.GetValueResult
import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceMetadata 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.PreferenceGetterFlags
import com.android.settingslib.graph.PreferenceGetterResponse import com.android.settingslib.graph.PreferenceGetterResponse
import com.android.settingslib.graph.PreferenceSetterResult 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.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceValueProto import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.graph.proto.TextProto import com.android.settingslib.graph.proto.TextProto
import com.android.settingslib.graph.textProto
import com.android.settingslib.graph.toProto import com.android.settingslib.graph.toProto
import com.android.settingslib.metadata.SensitivityLevel import com.android.settingslib.metadata.SensitivityLevel
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
@@ -54,6 +60,73 @@ class PreferenceServiceRequestTransformerTest {
@get:Rule @get:Rule
val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() 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 @Test
fun transformFrameworkGetValueRequest_returnsValidCatalystRequest() { fun transformFrameworkGetValueRequest_returnsValidCatalystRequest() {
val fRequest = GetValueRequest.Builder("screen", "pref").build() val fRequest = GetValueRequest.Builder("screen", "pref").build()