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.permission
18 
19 import android.content.pm.PermissionInfo
20 import android.util.Log
21 import com.android.modules.utils.BinaryXmlPullParser
22 import com.android.modules.utils.BinaryXmlSerializer
23 import com.android.server.permission.access.AccessState
24 import com.android.server.permission.access.UserState
25 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
26 import com.android.server.permission.access.util.attribute
27 import com.android.server.permission.access.util.attributeInt
28 import com.android.server.permission.access.util.attributeIntHex
29 import com.android.server.permission.access.util.attributeIntHexWithDefault
30 import com.android.server.permission.access.util.attributeInterned
31 import com.android.server.permission.access.util.forEachTag
32 import com.android.server.permission.access.util.getAttributeIntHexOrDefault
33 import com.android.server.permission.access.util.getAttributeIntHexOrThrow
34 import com.android.server.permission.access.util.getAttributeIntOrThrow
35 import com.android.server.permission.access.util.getAttributeValue
36 import com.android.server.permission.access.util.getAttributeValueOrThrow
37 import com.android.server.permission.access.util.tag
38 import com.android.server.permission.access.util.tagName
39 
40 class UidPermissionPersistence {
41     fun BinaryXmlPullParser.parseSystemState(state: AccessState) {
42         val systemState = state.systemState
43         when (tagName) {
44             TAG_PERMISSION_TREES -> parsePermissions(systemState.permissionTrees)
45             TAG_PERMISSIONS -> parsePermissions(systemState.permissions)
46             else -> {}
47         }
48     }
49 
50     private fun BinaryXmlPullParser.parsePermissions(permissions: IndexedMap<String, Permission>) {
51         forEachTag {
52             when (val tagName = tagName) {
53                 TAG_PERMISSION -> parsePermission(permissions)
54                 else -> Log.w(LOG_TAG, "Ignoring unknown tag $tagName when parsing permissions")
55             }
56         }
57     }
58 
59     private fun BinaryXmlPullParser.parsePermission(permissions: IndexedMap<String, Permission>) {
60         val name = getAttributeValueOrThrow(ATTR_NAME).intern()
61         @Suppress("DEPRECATION")
62         val permissionInfo = PermissionInfo().apply {
63             this.name = name
64             packageName = getAttributeValueOrThrow(ATTR_PACKAGE_NAME).intern()
65             protectionLevel = getAttributeIntHexOrThrow(ATTR_PROTECTION_LEVEL)
66         }
67         val type = getAttributeIntOrThrow(ATTR_TYPE)
68         when (type) {
69             Permission.TYPE_MANIFEST -> {}
70             Permission.TYPE_CONFIG -> {
71                 Log.w(LOG_TAG, "Ignoring unexpected config permission $name")
72                 return
73             }
74             Permission.TYPE_DYNAMIC -> {
75                 permissionInfo.apply {
76                     icon = getAttributeIntHexOrDefault(ATTR_ICON, 0)
77                     nonLocalizedLabel = getAttributeValue(ATTR_LABEL)
78                 }
79             }
80             else -> {
81                 Log.w(LOG_TAG, "Ignoring permission $name with unknown type $type")
82                 return
83             }
84         }
85         val permission = Permission(permissionInfo, false, type, 0)
86         permissions[name] = permission
87     }
88 
89     fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {
90         val systemState = state.systemState
91         serializePermissions(TAG_PERMISSION_TREES, systemState.permissionTrees)
92         serializePermissions(TAG_PERMISSIONS, systemState.permissions)
93     }
94 
95     private fun BinaryXmlSerializer.serializePermissions(
96         tagName: String,
97         permissions: IndexedMap<String, Permission>
98     ) {
99         tag(tagName) {
100             permissions.forEachValueIndexed { _, it -> serializePermission(it) }
101         }
102     }
103 
104     private fun BinaryXmlSerializer.serializePermission(permission: Permission) {
105         val type = permission.type
106         when (type) {
107             Permission.TYPE_MANIFEST, Permission.TYPE_DYNAMIC -> {}
108             Permission.TYPE_CONFIG -> return
109             else -> {
110                 Log.w(LOG_TAG, "Skipping serializing permission $name with unknown type $type")
111                 return
112             }
113         }
114         tag(TAG_PERMISSION) {
115             attributeInterned(ATTR_NAME, permission.name)
116             attributeInterned(ATTR_PACKAGE_NAME, permission.packageName)
117             attributeIntHex(ATTR_PROTECTION_LEVEL, permission.protectionLevel)
118             attributeInt(ATTR_TYPE, type)
119             if (type == Permission.TYPE_DYNAMIC) {
120                 val permissionInfo = permission.permissionInfo
121                 attributeIntHexWithDefault(ATTR_ICON, permissionInfo.icon, 0)
122                 permissionInfo.nonLocalizedLabel?.toString()?.let { attribute(ATTR_LABEL, it) }
123             }
124         }
125     }
126 
127     fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
128         when (tagName) {
129             TAG_PERMISSIONS -> parsePermissionFlags(state, userId)
130             else -> {}
131         }
132     }
133 
134     private fun BinaryXmlPullParser.parsePermissionFlags(state: AccessState, userId: Int) {
135         val userState = state.userStates[userId]
136         forEachTag {
137             when (tagName) {
138                 TAG_APP_ID -> parseAppId(userState)
139                 else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
140             }
141         }
142         userState.uidPermissionFlags.retainAllIndexed { _, appId, _ ->
143             val hasAppId = appId in state.systemState.appIds
144             if (!hasAppId) {
145                 Log.w(LOG_TAG, "Dropping unknown app ID $appId when parsing permission state")
146             }
147             hasAppId
148         }
149     }
150 
151     private fun BinaryXmlPullParser.parseAppId(userState: UserState) {
152         val appId = getAttributeIntOrThrow(ATTR_ID)
153         val permissionFlags = IndexedMap<String, Int>()
154         userState.uidPermissionFlags[appId] = permissionFlags
155         parseAppIdPermissions(permissionFlags)
156     }
157 
158     private fun BinaryXmlPullParser.parseAppIdPermissions(
159         permissionFlags: IndexedMap<String, Int>
160     ) {
161         forEachTag {
162             when (tagName) {
163                 TAG_PERMISSION -> parseAppIdPermission(permissionFlags)
164                 else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
165             }
166         }
167     }
168 
169     private fun BinaryXmlPullParser.parseAppIdPermission(permissionFlags: IndexedMap<String, Int>) {
170         val name = getAttributeValueOrThrow(ATTR_NAME).intern()
171         val flags = getAttributeIntOrThrow(ATTR_FLAGS)
172         permissionFlags[name] = flags
173     }
174 
175     fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
176         serializePermissionFlags(state.userStates[userId])
177     }
178 
179     private fun BinaryXmlSerializer.serializePermissionFlags(userState: UserState) {
180         tag(TAG_PERMISSIONS) {
181             userState.uidPermissionFlags.forEachIndexed { _, appId, permissionFlags ->
182                 serializeAppId(appId, permissionFlags)
183             }
184         }
185     }
186 
187     private fun BinaryXmlSerializer.serializeAppId(
188         appId: Int,
189         permissionFlags: IndexedMap<String, Int>
190     ) {
191         tag(TAG_APP_ID) {
192             attributeInt(ATTR_ID, appId)
193             serializeAppIdPermissions(permissionFlags)
194         }
195     }
196 
197     private fun BinaryXmlSerializer.serializeAppIdPermissions(
198         permissionFlags: IndexedMap<String, Int>
199     ) {
200         permissionFlags.forEachIndexed { _, name, flags ->
201             serializeAppIdPermission(name, flags)
202         }
203     }
204 
205     private fun BinaryXmlSerializer.serializeAppIdPermission(name: String, flags: Int) {
206         tag(TAG_PERMISSION) {
207             attributeInterned(ATTR_NAME, name)
208             attributeInt(ATTR_FLAGS, flags)
209         }
210     }
211 
212     companion object {
213         private val LOG_TAG = UidPermissionPersistence::class.java.simpleName
214 
215         private const val TAG_APP_ID = "app-id"
216         private const val TAG_PERMISSION = "permission"
217         private const val TAG_PERMISSIONS = "permissions"
218         private const val TAG_PERMISSION_TREES = "permission-trees"
219 
220         private const val ATTR_FLAGS = "flags"
221         private const val ATTR_ICON = "icon"
222         private const val ATTR_ID = "id"
223         private const val ATTR_LABEL = "label"
224         private const val ATTR_NAME = "name"
225         private const val ATTR_PACKAGE_NAME = "packageName"
226         private const val ATTR_PROTECTION_LEVEL = "protectionLevel"
227         private const val ATTR_TYPE = "type"
228     }
229 }
230