1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.service 18 19 import android.Manifest.permission.WRITE_SYSTEM_PREFERENCES 20 import android.app.AppOpsManager.OP_WRITE_SYSTEM_PREFERENCES 21 import android.os.Binder 22 import android.os.OutcomeReceiver 23 import android.service.settings.preferences.GetValueRequest 24 import android.service.settings.preferences.GetValueResult 25 import android.service.settings.preferences.MetadataRequest 26 import android.service.settings.preferences.MetadataResult 27 import android.service.settings.preferences.SetValueRequest 28 import android.service.settings.preferences.SetValueResult 29 import android.service.settings.preferences.SettingsPreferenceService 30 import android.util.Log 31 import com.android.settings.metrics.SettingsRemoteOpMetricsLogger 32 import com.android.settingslib.graph.GetPreferenceGraphApiHandler 33 import com.android.settingslib.graph.GetPreferenceGraphRequest 34 import com.android.settingslib.graph.PreferenceGetterApiHandler 35 import com.android.settingslib.graph.PreferenceGetterFlags 36 import com.android.settingslib.graph.PreferenceSetterApiHandler 37 import com.android.settingslib.ipc.ApiPermissionChecker 38 import com.android.settingslib.ipc.AppOpApiPermissionChecker 39 import kotlinx.coroutines.CoroutineScope 40 import kotlinx.coroutines.Dispatchers 41 import kotlinx.coroutines.SupervisorJob 42 import kotlinx.coroutines.launch 43 44 class PreferenceService : SettingsPreferenceService() { 45 46 private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) 47 48 private val getApiHandler: PreferenceGetterApiHandler 49 private val setApiHandler: PreferenceSetterApiHandler 50 private val graphApi: GetPreferenceGraphApiHandler 51 52 init { 53 val metricsLogger = SettingsRemoteOpMetricsLogger() 54 // PreferenceService specifies READ_SYSTEM_PREFERENCES permission in AndroidManifest.xml 55 getApiHandler = 56 PreferenceGetterApiHandler(1, ApiPermissionChecker.alwaysAllow(), metricsLogger) 57 setApiHandler = 58 PreferenceSetterApiHandler( 59 2, 60 AppOpApiPermissionChecker(OP_WRITE_SYSTEM_PREFERENCES, WRITE_SYSTEM_PREFERENCES), 61 metricsLogger, 62 ) 63 graphApi = 64 GetPreferenceGraphApiHandler(3, ApiPermissionChecker.alwaysAllow(), metricsLogger) 65 } 66 onGetAllPreferenceMetadatanull67 override fun onGetAllPreferenceMetadata( 68 request: MetadataRequest, 69 callback: OutcomeReceiver<MetadataResult, Exception>, 70 ) { 71 // MUST get pid/uid in binder thread 72 val callingPid = Binder.getCallingPid() 73 val callingUid = Binder.getCallingUid() 74 Log.i(TAG, "GetAllPreferenceMetadata pid=$callingPid uid=$callingUid") 75 scope.launch { 76 val graphProto = 77 graphApi.invoke( 78 application, 79 callingPid, 80 callingUid, 81 GetPreferenceGraphRequest(flags = PreferenceGetterFlags.METADATA), 82 ) 83 val result = transformCatalystGetMetadataResponse(this@PreferenceService, graphProto) 84 callback.onResult(result) 85 } 86 } 87 onGetPreferenceValuenull88 override fun onGetPreferenceValue( 89 request: GetValueRequest, 90 callback: OutcomeReceiver<GetValueResult, Exception>, 91 ) { 92 // MUST get pid/uid in binder thread 93 val callingPid = Binder.getCallingPid() 94 val callingUid = Binder.getCallingUid() 95 Log.i(TAG, "GetPreferenceValue pid=$callingPid uid=$callingUid") 96 scope.launch { 97 val apiRequest = transformFrameworkGetValueRequest(request) 98 val response = getApiHandler.invoke(application, callingPid, callingUid, apiRequest) 99 val result = 100 transformCatalystGetValueResponse(this@PreferenceService, request, response) 101 if (result == null) { 102 callback.onError(IllegalStateException("No response")) 103 } else { 104 callback.onResult(result) 105 } 106 } 107 } 108 onSetPreferenceValuenull109 override fun onSetPreferenceValue( 110 request: SetValueRequest, 111 callback: OutcomeReceiver<SetValueResult, Exception>, 112 ) { 113 // MUST get pid/uid in binder thread 114 val callingPid = Binder.getCallingPid() 115 val callingUid = Binder.getCallingUid() 116 Log.i(TAG, "SetPreferenceValue pid=$callingPid uid=$callingUid") 117 scope.launch { 118 val apiRequest = transformFrameworkSetValueRequest(request) 119 if (apiRequest == null) { 120 callback.onResult( 121 SetValueResult.Builder(SetValueResult.RESULT_INVALID_REQUEST).build() 122 ) 123 } else { 124 val response = setApiHandler.invoke(application, callingPid, callingUid, apiRequest) 125 126 callback.onResult(transformCatalystSetValueResponse(response)) 127 } 128 } 129 } 130 131 companion object { 132 private const val TAG = "PreferenceService" 133 } 134 } 135