Loading src/com/android/settings/service/PreferenceService.kt +30 −8 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading @@ -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( Loading @@ -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 } } src/com/android/settings/service/PreferenceServiceRequestTransformer.kt +52 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading Loading @@ -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 Loading tests/robotests/src/com/android/settings/service/PreferenceServiceRequestTransformerTest.kt +74 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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() Loading Loading
src/com/android/settings/service/PreferenceService.kt +30 −8 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading @@ -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( Loading @@ -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 } }
src/com/android/settings/service/PreferenceServiceRequestTransformer.kt +52 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading Loading @@ -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 Loading
tests/robotests/src/com/android/settings/service/PreferenceServiceRequestTransformerTest.kt +74 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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() Loading