xref: /aosp_15_r20/frameworks/base/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
<lambda>null2  * Copyright (C) 2022 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.server.permission.access.appop
18 
19 import android.app.AppOpsManager
20 import android.companion.virtual.VirtualDeviceManager
21 import android.os.Binder
22 import android.os.Handler
23 import android.os.UserHandle
24 import android.permission.PermissionManager
25 import android.permission.flags.Flags
26 import android.util.ArrayMap
27 import android.util.ArraySet
28 import android.util.LongSparseArray
29 import android.util.Slog
30 import android.util.SparseArray
31 import android.util.SparseBooleanArray
32 import android.util.SparseIntArray
33 import com.android.internal.annotations.VisibleForTesting
34 import com.android.internal.util.IntPair
35 import com.android.server.appop.AppOpsCheckingServiceInterface
36 import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener
37 import com.android.server.permission.access.AccessCheckingService
38 import com.android.server.permission.access.AppOpUri
39 import com.android.server.permission.access.DevicePermissionUri
40 import com.android.server.permission.access.GetStateScope
41 import com.android.server.permission.access.PackageUri
42 import com.android.server.permission.access.PermissionUri
43 import com.android.server.permission.access.UidUri
44 import com.android.server.permission.access.appop.AppOpModes.MODE_ALLOWED
45 import com.android.server.permission.access.appop.AppOpModes.MODE_FOREGROUND
46 import com.android.server.permission.access.appop.AppOpModes.MODE_IGNORED
47 import com.android.server.permission.access.collection.forEachIndexed
48 import com.android.server.permission.access.collection.set
49 import com.android.server.permission.access.permission.AppIdPermissionPolicy
50 import com.android.server.permission.access.permission.DevicePermissionPolicy
51 import com.android.server.permission.access.permission.PermissionFlags
52 import com.android.server.permission.access.permission.PermissionService
53 
54 class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingServiceInterface {
55     private val packagePolicy =
56         service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy
57     private val appIdPolicy =
58         service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy
59     private val permissionPolicy =
60         service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
61     private val devicePermissionPolicy =
62         service.getSchemePolicy(UidUri.SCHEME, DevicePermissionUri.SCHEME) as DevicePermissionPolicy
63 
64     private val context = service.context
65 
66     // Maps appop code to its runtime permission
67     private val runtimeAppOpToPermissionNames = SparseArray<String>()
68 
69     // Maps runtime permission to its appop codes
70     private val runtimePermissionNameToAppOp = ArrayMap<String, Int>()
71 
72     private var foregroundableOps = SparseBooleanArray()
73 
74     /* Maps foreground permissions to their background permission. Background permissions aren't
75     required to be runtime */
76     private val foregroundToBackgroundPermissionName = ArrayMap<String, String>()
77 
78     /* Maps background permissions to their foreground permissions. Background permissions aren't
79     required to be runtime */
80     private val backgroundToForegroundPermissionNames = ArrayMap<String, ArraySet<String>>()
81 
82     private lateinit var handler: Handler
83 
84     @Volatile private var listeners = ArraySet<AppOpsModeChangedListener>()
85     private val listenersLock = Any()
86 
87     fun initialize() {
88         // TODO(b/252883039): Wrong handler. Inject main thread handler here.
89         handler = Handler(context.mainLooper)
90 
91         appIdPolicy.addOnAppOpModeChangedListener(OnAppIdAppOpModeChangedListener())
92         packagePolicy.addOnAppOpModeChangedListener(OnPackageAppOpModeChangedListener())
93     }
94 
95     @VisibleForTesting
96     override fun writeState() {
97         // Not implemented because writes are handled automatically.
98     }
99 
100     override fun readState() {
101         // Not implemented because reads are handled automatically.
102     }
103 
104     @VisibleForTesting
105     override fun shutdown() {
106         // Not implemented because writes are handled automatically.
107     }
108 
109     override fun systemReady() {
110         if (Flags.runtimePermissionAppopsMappingEnabled()) {
111             createPermissionAppOpMapping()
112             val permissionListener = OnPermissionFlagsChangedListener()
113             permissionPolicy.addOnPermissionFlagsChangedListener(permissionListener)
114             devicePermissionPolicy.addOnPermissionFlagsChangedListener(permissionListener)
115         }
116     }
117 
118     private fun createPermissionAppOpMapping() {
119         val permissions = service.getState { with(permissionPolicy) { getPermissions() } }
120 
121         for (appOpCode in 0 until AppOpsManager._NUM_OP) {
122             AppOpsManager.opToPermission(appOpCode)?.let { permissionName ->
123                 // Multiple ops might map to a single permission but only one is considered the
124                 // runtime appop calculations.
125                 if (appOpCode == AppOpsManager.permissionToOpCode(permissionName)) {
126                     val permission = permissions[permissionName]!!
127                     if (permission.isRuntime) {
128                         runtimePermissionNameToAppOp[permissionName] = appOpCode
129                         runtimeAppOpToPermissionNames[appOpCode] = permissionName
130                         permission.permissionInfo.backgroundPermission?.let {
131                             backgroundPermissionName ->
132                             // Note: background permission may not be runtime,
133                             // e.g. microphone/camera.
134                             foregroundableOps[appOpCode] = true
135                             foregroundToBackgroundPermissionName[permissionName] =
136                                 backgroundPermissionName
137                             backgroundToForegroundPermissionNames
138                                 .getOrPut(backgroundPermissionName, ::ArraySet)
139                                 .add(permissionName)
140                         }
141                     }
142                 }
143             }
144         }
145     }
146 
147     override fun getNonDefaultUidModes(uid: Int, deviceId: String): SparseIntArray {
148         val appId = UserHandle.getAppId(uid)
149         val userId = UserHandle.getUserId(uid)
150         service.getState {
151             val modes =
152                 with(appIdPolicy) { opNameMapToOpSparseArray(getAppOpModes(appId, userId)?.map) }
153             if (Flags.runtimePermissionAppopsMappingEnabled()) {
154                 runtimePermissionNameToAppOp.forEachIndexed { _, permissionName, appOpCode ->
155                     val mode =
156                         getUidModeFromPermissionState(appId, userId, permissionName, deviceId)
157                     if (mode != AppOpsManager.opToDefaultMode(appOpCode)) {
158                         modes[appOpCode] = mode
159                     }
160                 }
161             }
162 
163             return modes
164         }
165     }
166 
167     override fun getNonDefaultPackageModes(packageName: String, userId: Int): SparseIntArray {
168         return opNameMapToOpSparseArray(getPackageModes(packageName, userId))
169     }
170 
171     override fun getUidMode(uid: Int, deviceId: String, op: Int): Int {
172         val appId = UserHandle.getAppId(uid)
173         val userId = UserHandle.getUserId(uid)
174         val opName = AppOpsManager.opToPublicName(op)
175         val permissionName = runtimeAppOpToPermissionNames[op]
176 
177         return if (!Flags.runtimePermissionAppopsMappingEnabled() || permissionName == null) {
178             service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } }
179         } else {
180             service.getState {
181                 getUidModeFromPermissionState(appId, userId, permissionName, deviceId)
182             }
183         }
184     }
185 
186     private fun getUidModes(uid: Int): ArrayMap<String, Int>? {
187         val appId = UserHandle.getAppId(uid)
188         val userId = UserHandle.getUserId(uid)
189         return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map
190     }
191 
192     private fun GetStateScope.getUidModeFromPermissionState(
193         appId: Int,
194         userId: Int,
195         permissionName: String,
196         deviceId: String
197     ): Int {
198         val checkDevicePermissionFlags =
199             deviceId != VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT &&
200                 permissionName in PermissionManager.DEVICE_AWARE_PERMISSIONS
201         val permissionFlags =
202             if (checkDevicePermissionFlags) {
203                 with(devicePermissionPolicy) {
204                     getPermissionFlags(appId, deviceId, userId, permissionName)
205                 }
206             } else {
207                 with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) }
208             }
209         val backgroundPermissionName = foregroundToBackgroundPermissionName[permissionName]
210         val backgroundPermissionFlags =
211             if (backgroundPermissionName != null) {
212                 if (checkDevicePermissionFlags) {
213                     with(devicePermissionPolicy) {
214                         getPermissionFlags(appId, deviceId, userId, backgroundPermissionName)
215                     }
216                 } else {
217                     with(permissionPolicy) {
218                         getPermissionFlags(appId, userId, backgroundPermissionName)
219                     }
220                 }
221             } else {
222                 PermissionFlags.RUNTIME_GRANTED
223             }
224         val result = evaluateModeFromPermissionFlags(permissionFlags, backgroundPermissionFlags)
225         if (result != MODE_IGNORED) {
226             return result
227         }
228 
229         val fullerPermissionName =
230             PermissionService.getFullerPermission(permissionName) ?: return result
231         return getUidModeFromPermissionState(appId, userId, fullerPermissionName, deviceId)
232     }
233 
234     private fun evaluateModeFromPermissionFlags(
235         foregroundFlags: Int,
236         backgroundFlags: Int = PermissionFlags.RUNTIME_GRANTED
237     ): Int =
238         if (PermissionFlags.isAppOpGranted(foregroundFlags)) {
239             if (PermissionFlags.isAppOpGranted(backgroundFlags)) {
240                 MODE_ALLOWED
241             } else {
242                 MODE_FOREGROUND
243             }
244         } else {
245             MODE_IGNORED
246         }
247 
248     override fun setUidMode(uid: Int, deviceId: String, code: Int, mode: Int): Boolean {
249         val appId = UserHandle.getAppId(uid)
250         val userId = UserHandle.getUserId(uid)
251         val appOpName = AppOpsManager.opToPublicName(code)
252 
253         if (
254             Flags.runtimePermissionAppopsMappingEnabled() && code in runtimeAppOpToPermissionNames
255         ) {
256             val oldMode =
257                 service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, appOpName) } }
258             val wouldHaveChanged = oldMode != mode
259             val logMessage =
260                 (if (wouldHaveChanged) "Blocked" else "Ignored") +
261                     " setUidMode call for runtime permission app op:" +
262                     " uid = $uid," +
263                     " code = ${AppOpsManager.opToName(code)}," +
264                     " mode = ${AppOpsManager.modeToName(mode)}," +
265                     " callingUid = ${Binder.getCallingUid()}," +
266                     " oldMode = ${AppOpsManager.modeToName(oldMode)}"
267             if (wouldHaveChanged) {
268                 Slog.e(LOG_TAG, logMessage, RuntimeException())
269             } else {
270                 Slog.w(LOG_TAG, logMessage)
271             }
272             return false
273         }
274 
275         var wasChanged: Boolean
276         service.mutateState {
277             wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) }
278         }
279         return wasChanged
280     }
281 
282     override fun getPackageMode(packageName: String, op: Int, userId: Int): Int {
283         val opName = AppOpsManager.opToPublicName(op)
284         return service.getState {
285             with(packagePolicy) { getAppOpMode(packageName, userId, opName) }
286         }
287     }
288 
289     private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? =
290         service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map
291 
292     override fun setPackageMode(packageName: String, appOpCode: Int, mode: Int, userId: Int) {
293         val appOpName = AppOpsManager.opToPublicName(appOpCode)
294 
295         if (
296             Flags.runtimePermissionAppopsMappingEnabled() &&
297                 appOpCode in runtimeAppOpToPermissionNames
298         ) {
299             Slog.w(
300                 LOG_TAG,
301                 "(packageName=$packageName, userId=$userId)'s appop state" +
302                     " for runtime op $appOpName should not be set directly.",
303                 RuntimeException()
304             )
305             return
306         }
307         service.mutateState {
308             with(packagePolicy) { setAppOpMode(packageName, userId, appOpName, mode) }
309         }
310     }
311 
312     override fun removeUid(uid: Int) {
313         val appId = UserHandle.getAppId(uid)
314         val userId = UserHandle.getUserId(uid)
315         service.mutateState { with(appIdPolicy) { removeAppOpModes(appId, userId) } }
316     }
317 
318     override fun removePackage(packageName: String, userId: Int): Boolean {
319         var wasChanged: Boolean
320         service.mutateState {
321             wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) }
322         }
323         return wasChanged
324     }
325 
326     private fun opNameMapToOpSparseArray(modes: ArrayMap<String, Int>?): SparseIntArray =
327         if (modes == null) {
328             SparseIntArray()
329         } else {
330             val opSparseArray = SparseIntArray(modes.size)
331             modes.forEachIndexed { _, opName, opMode ->
332                 opSparseArray.put(AppOpsManager.strOpToOp(opName), opMode)
333             }
334             opSparseArray
335         }
336 
337     override fun clearAllModes() {
338         // We don't need to implement this because it's only called in AppOpsService#readState
339         // and we have our own persistence.
340     }
341 
342     override fun getForegroundOps(uid: Int, deviceId: String): SparseBooleanArray {
343         return SparseBooleanArray().apply {
344             getUidModes(uid)?.forEachIndexed { _, op, mode ->
345                 if (mode == AppOpsManager.MODE_FOREGROUND) {
346                     this[AppOpsManager.strOpToOp(op)] = true
347                 }
348             }
349             if (Flags.runtimePermissionAppopsMappingEnabled()) {
350                 foregroundableOps.forEachIndexed { _, op, _ ->
351                     if (getUidMode(uid, deviceId, op) == AppOpsManager.MODE_FOREGROUND) {
352                         this[op] = true
353                     }
354                 }
355             }
356         }
357     }
358 
359     override fun getForegroundOps(packageName: String, userId: Int): SparseBooleanArray {
360         return SparseBooleanArray().apply {
361             getPackageModes(packageName, userId)?.forEachIndexed { _, op, mode ->
362                 if (mode == AppOpsManager.MODE_FOREGROUND) {
363                     this[AppOpsManager.strOpToOp(op)] = true
364                 }
365             }
366             if (Flags.runtimePermissionAppopsMappingEnabled()) {
367                 foregroundableOps.forEachIndexed { _, op, _ ->
368                     if (getPackageMode(packageName, op, userId) == AppOpsManager.MODE_FOREGROUND) {
369                         this[op] = true
370                     }
371                 }
372             }
373         }
374     }
375 
376     override fun addAppOpsModeChangedListener(listener: AppOpsModeChangedListener): Boolean {
377         synchronized(listenersLock) {
378             val newListeners = ArraySet(listeners)
379             val result = newListeners.add(listener)
380             listeners = newListeners
381             return result
382         }
383     }
384 
385     override fun removeAppOpsModeChangedListener(listener: AppOpsModeChangedListener): Boolean {
386         synchronized(listenersLock) {
387             val newListeners = ArraySet(listeners)
388             val result = newListeners.remove(listener)
389             listeners = newListeners
390             return result
391         }
392     }
393 
394     private inner class OnAppIdAppOpModeChangedListener :
395         AppIdAppOpPolicy.OnAppOpModeChangedListener() {
396         // (uid, appOpCode) -> newMode
397         private val pendingChanges = LongSparseArray<Int>()
398 
399         override fun onAppOpModeChanged(
400             appId: Int,
401             userId: Int,
402             appOpName: String,
403             oldMode: Int,
404             newMode: Int
405         ) {
406             val uid = UserHandle.getUid(userId, appId)
407             val appOpCode = AppOpsManager.strOpToOp(appOpName)
408             val key = IntPair.of(uid, appOpCode)
409 
410             pendingChanges[key] = newMode
411         }
412 
413         override fun onStateMutated() {
414             val listenersLocal = listeners
415             pendingChanges.forEachIndexed { _, key, mode ->
416                 listenersLocal.forEachIndexed { _, listener ->
417                     val uid = IntPair.first(key)
418                     val appOpCode = IntPair.second(key)
419 
420                     listener.onUidModeChanged(
421                         uid,
422                         appOpCode,
423                         mode,
424                         VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
425                     )
426                 }
427             }
428 
429             pendingChanges.clear()
430         }
431     }
432 
433     private inner class OnPackageAppOpModeChangedListener :
434         PackageAppOpPolicy.OnAppOpModeChangedListener() {
435         // (packageName, userId, appOpCode) -> newMode
436         private val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>()
437 
438         override fun onAppOpModeChanged(
439             packageName: String,
440             userId: Int,
441             appOpName: String,
442             oldMode: Int,
443             newMode: Int
444         ) {
445             val appOpCode = AppOpsManager.strOpToOp(appOpName)
446             val key = Triple(packageName, userId, appOpCode)
447 
448             pendingChanges[key] = newMode
449         }
450 
451         override fun onStateMutated() {
452             val listenersLocal = listeners
453             pendingChanges.forEachIndexed { _, key, mode ->
454                 listenersLocal.forEachIndexed { _, listener ->
455                     val packageName = key.first
456                     val userId = key.second
457                     val appOpCode = key.third
458 
459                     listener.onPackageModeChanged(packageName, userId, appOpCode, mode)
460                 }
461             }
462 
463             pendingChanges.clear()
464         }
465     }
466 
467     private inner class OnPermissionFlagsChangedListener :
468         AppIdPermissionPolicy.OnPermissionFlagsChangedListener,
469         DevicePermissionPolicy.OnDevicePermissionFlagsChangedListener {
470         // (uid, deviceId, appOpCode) -> newMode
471         private val pendingChanges = ArrayMap<Triple<Int, String, Int>, Int>()
472 
473         override fun onPermissionFlagsChanged(
474             appId: Int,
475             userId: Int,
476             permissionName: String,
477             oldFlags: Int,
478             newFlags: Int
479         ) {
480             onDevicePermissionFlagsChanged(
481                 appId,
482                 userId,
483                 VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT,
484                 permissionName,
485                 oldFlags,
486                 newFlags
487             )
488         }
489 
490         override fun onDevicePermissionFlagsChanged(
491             appId: Int,
492             userId: Int,
493             deviceId: String,
494             permissionName: String,
495             oldFlags: Int,
496             newFlags: Int
497         ) {
498             backgroundToForegroundPermissionNames[permissionName]?.let { foregroundPermissions ->
499                 // This is a background permission; there may be multiple foreground permissions
500                 // affected.
501                 foregroundPermissions.forEachIndexed { _, foregroundPermissionName ->
502                     runtimePermissionNameToAppOp[foregroundPermissionName]?.let { appOpCode ->
503                         val foregroundPermissionFlags =
504                             getPermissionFlags(appId, userId, foregroundPermissionName)
505                         addPendingChangedModeIfNeeded(
506                             appId,
507                             userId,
508                             deviceId,
509                             appOpCode,
510                             foregroundPermissionFlags,
511                             oldFlags,
512                             foregroundPermissionFlags,
513                             newFlags
514                         )
515                     }
516                 }
517             }
518                 ?: foregroundToBackgroundPermissionName[permissionName]?.let { backgroundPermission
519                     ->
520                     runtimePermissionNameToAppOp[permissionName]?.let { appOpCode ->
521                         val backgroundPermissionFlags =
522                             getPermissionFlags(appId, userId, backgroundPermission)
523                         addPendingChangedModeIfNeeded(
524                             appId,
525                             userId,
526                             deviceId,
527                             appOpCode,
528                             oldFlags,
529                             backgroundPermissionFlags,
530                             newFlags,
531                             backgroundPermissionFlags
532                         )
533                     }
534                 }
535                     ?: runtimePermissionNameToAppOp[permissionName]?.let { appOpCode ->
536                     addPendingChangedModeIfNeeded(
537                         appId,
538                         userId,
539                         deviceId,
540                         appOpCode,
541                         oldFlags,
542                         PermissionFlags.RUNTIME_GRANTED,
543                         newFlags,
544                         PermissionFlags.RUNTIME_GRANTED
545                     )
546                 }
547         }
548 
549         private fun getPermissionFlags(appId: Int, userId: Int, permissionName: String): Int =
550             service.getState {
551                 with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) }
552             }
553 
554         private fun addPendingChangedModeIfNeeded(
555             appId: Int,
556             userId: Int,
557             deviceId: String,
558             appOpCode: Int,
559             oldForegroundFlags: Int,
560             oldBackgroundFlags: Int,
561             newForegroundFlags: Int,
562             newBackgroundFlags: Int,
563         ) {
564             val oldMode = evaluateModeFromPermissionFlags(oldForegroundFlags, oldBackgroundFlags)
565             val newMode = evaluateModeFromPermissionFlags(newForegroundFlags, newBackgroundFlags)
566 
567             if (oldMode != newMode) {
568                 val uid = UserHandle.getUid(userId, appId)
569                 pendingChanges[Triple(uid, deviceId, appOpCode)] = newMode
570             }
571         }
572 
573         override fun onStateMutated() {
574             val listenersLocal = listeners
575             pendingChanges.forEachIndexed { _, key, mode ->
576                 listenersLocal.forEachIndexed { _, listener ->
577                     val uid = key.first
578                     val deviceId = key.second
579                     val appOpCode = key.third
580 
581                     listener.onUidModeChanged(uid, appOpCode, mode, deviceId)
582                 }
583             }
584 
585             pendingChanges.clear()
586         }
587     }
588 
589     companion object {
590         private val LOG_TAG = AppOpService::class.java.simpleName
591     }
592 }
593