1 /*
2  * 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 com.android.server.permission.access.AccessUri
21 import com.android.server.permission.access.AppOpUri
22 import com.android.server.permission.access.GetStateScope
23 import com.android.server.permission.access.MutateStateScope
24 import com.android.server.permission.access.PackageUri
25 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
26 
27 class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) {
28     @Volatile
29     private var onAppOpModeChangedListeners = IndexedListSet<OnAppOpModeChangedListener>()
30     private val onAppOpModeChangedListenersLock = Any()
31 
32     override val subjectScheme: String
33         get() = PackageUri.SCHEME
34 
35     override fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int {
36         subject as PackageUri
37         `object` as AppOpUri
38         return getAppOpMode(subject.packageName, subject.userId, `object`.appOpName)
39     }
40 
41     override fun MutateStateScope.setDecision(
42         subject: AccessUri,
43         `object`: AccessUri,
44         decision: Int
45     ) {
46         subject as PackageUri
47         `object` as AppOpUri
48         setAppOpMode(subject.packageName, subject.userId, `object`.appOpName, decision)
49     }
50 
51     override fun GetStateScope.onStateMutated() {
52         onAppOpModeChangedListeners.forEachIndexed { _, it -> it.onStateMutated() }
53     }
54 
55     override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {
56         newState.userStates.forEachIndexed { _, _, userState ->
57             userState.packageAppOpModes -= packageName
58             userState.requestWrite()
59             // Skip notifying the change listeners since the package no longer exists.
60         }
61     }
62 
63     fun GetStateScope.getAppOpModes(packageName: String, userId: Int): IndexedMap<String, Int>? =
64         state.userStates[userId].packageAppOpModes[packageName]
65 
66     fun MutateStateScope.removeAppOpModes(packageName: String, userId: Int): Boolean {
67         val userState = newState.userStates[userId]
68         val isChanged = userState.packageAppOpModes.remove(packageName) != null
69         if (isChanged) {
70             userState.requestWrite()
71         }
72         return isChanged
73     }
74 
75     fun GetStateScope.getAppOpMode(packageName: String, userId: Int, appOpName: String): Int =
76         state.userStates[userId].packageAppOpModes[packageName]
77             .getWithDefault(appOpName, AppOpsManager.opToDefaultMode(appOpName))
78 
79     fun MutateStateScope.setAppOpMode(
80         packageName: String,
81         userId: Int,
82         appOpName: String,
83         mode: Int
84     ): Boolean {
85         val userState = newState.userStates[userId]
86         val packageAppOpModes = userState.packageAppOpModes
87         var appOpModes = packageAppOpModes[packageName]
88         val defaultMode = AppOpsManager.opToDefaultMode(appOpName)
89         val oldMode = appOpModes.getWithDefault(appOpName, defaultMode)
90         if (oldMode == mode) {
91             return false
92         }
93         if (appOpModes == null) {
94             appOpModes = IndexedMap()
95             packageAppOpModes[packageName] = appOpModes
96         }
97         appOpModes.putWithDefault(appOpName, mode, defaultMode)
98         if (appOpModes.isEmpty()) {
99             packageAppOpModes -= packageName
100         }
101         userState.requestWrite()
102         onAppOpModeChangedListeners.forEachIndexed { _, it ->
103             it.onAppOpModeChanged(packageName, userId, appOpName, oldMode, mode)
104         }
105         return true
106     }
107 
108     fun addOnAppOpModeChangedListener(listener: OnAppOpModeChangedListener) {
109         synchronized(onAppOpModeChangedListenersLock) {
110             onAppOpModeChangedListeners = onAppOpModeChangedListeners + listener
111         }
112     }
113 
114     fun removeOnAppOpModeChangedListener(listener: OnAppOpModeChangedListener) {
115         synchronized(onAppOpModeChangedListenersLock) {
116             onAppOpModeChangedListeners = onAppOpModeChangedListeners - listener
117         }
118     }
119 
120     /**
121      * Listener for app op mode changes.
122      */
123     abstract class OnAppOpModeChangedListener {
124         /**
125          * Called when an app op mode change has been made to the upcoming new state.
126          *
127          * Implementations should keep this method fast to avoid stalling the locked state mutation,
128          * and only call external code after [onStateMutated] when the new state has actually become
129          * the current state visible to external code.
130          */
131         abstract fun onAppOpModeChanged(
132             packageName: String,
133             userId: Int,
134             appOpName: String,
135             oldMode: Int,
136             newMode: Int
137         )
138 
139         /**
140          * Called when the upcoming new state has become the current state.
141          *
142          * Implementations should keep this method fast to avoid stalling the locked state mutation.
143          */
144         abstract fun onStateMutated()
145     }
146 }
147