• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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