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.util.Log
20 import com.android.modules.utils.BinaryXmlPullParser
21 import com.android.modules.utils.BinaryXmlSerializer
22 import com.android.server.permission.access.AccessState
23 import com.android.server.permission.access.UserState
24 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
25 import com.android.server.permission.access.util.attributeInterned
26 import com.android.server.permission.access.util.forEachTag
27 import com.android.server.permission.access.util.getAttributeValueOrThrow
28 import com.android.server.permission.access.util.tag
29 import com.android.server.permission.access.util.tagName
30 
31 class PackageAppOpPersistence : BaseAppOpPersistence() {
32     override fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
33         when (tagName) {
34             TAG_PACKAGE_APP_OPS -> parsePackageAppOps(state, userId)
35             else -> {}
36         }
37     }
38 
39     private fun BinaryXmlPullParser.parsePackageAppOps(state: AccessState, userId: Int) {
40         val userState = state.userStates[userId]
41         forEachTag {
42             when (tagName) {
43                 TAG_PACKAGE -> parsePackage(userState)
44                 else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing app-op state")
45             }
46         }
47         userState.packageAppOpModes.retainAllIndexed { _, packageName, _ ->
48             val hasPackage = packageName in state.systemState.packageStates
49             if (!hasPackage) {
50                 Log.w(LOG_TAG, "Dropping unknown package $packageName when parsing app-op state")
51             }
52             hasPackage
53         }
54     }
55 
56     private fun BinaryXmlPullParser.parsePackage(userState: UserState) {
57         val packageName = getAttributeValueOrThrow(ATTR_NAME).intern()
58         val appOpModes = IndexedMap<String, Int>()
59         userState.packageAppOpModes[packageName] = appOpModes
60         parseAppOps(appOpModes)
61     }
62 
63     override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
64         serializePackageAppOps(state.userStates[userId])
65     }
66 
67     private fun BinaryXmlSerializer.serializePackageAppOps(userState: UserState) {
68         tag(TAG_PACKAGE_APP_OPS) {
69             userState.packageAppOpModes.forEachIndexed { _, packageName, appOpModes ->
70                 serializePackage(packageName, appOpModes)
71             }
72         }
73     }
74 
75     private fun BinaryXmlSerializer.serializePackage(
76         packageName: String,
77         appOpModes: IndexedMap<String, Int>
78     ) {
79         tag(TAG_PACKAGE) {
80             attributeInterned(ATTR_NAME, packageName)
81             serializeAppOps(appOpModes)
82         }
83     }
84 
85     companion object {
86         private val LOG_TAG = PackageAppOpPersistence::class.java.simpleName
87 
88         private const val TAG_PACKAGE = "package"
89         private const val TAG_PACKAGE_APP_OPS = "package-app-ops"
90 
91         private const val ATTR_NAME = "name"
92     }
93 }
94