1 /*
<lambda>null2  * Copyright (C) 2023 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.util.Slog
20 import com.android.server.LocalServices
21 import com.android.server.permission.access.MutableAccessState
22 import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
23 import com.android.server.permission.access.util.PackageVersionMigration
24 import com.android.server.pm.permission.PermissionMigrationHelper
25 
26 /** This class migrate legacy permissions to unified permission subsystem */
27 class AppIdPermissionMigration {
28     internal fun migrateSystemState(state: MutableAccessState) {
29         val legacyPermissionsManager =
30             LocalServices.getService(PermissionMigrationHelper::class.java)!!
31         if (!legacyPermissionsManager.hasLegacyPermission()) {
32             return
33         }
34 
35         migratePermissions(
36             state.mutateSystemState().mutatePermissions(),
37             legacyPermissionsManager.legacyPermissions
38         )
39         migratePermissions(
40             state.mutateSystemState().mutatePermissionTrees(),
41             legacyPermissionsManager.legacyPermissionTrees,
42             true
43         )
44     }
45 
46     private fun migratePermissions(
47         permissions: MutableIndexedMap<String, Permission>,
48         legacyPermissions: Map<String, PermissionMigrationHelper.LegacyPermission>,
49         isPermissionTree: Boolean = false
50     ) {
51         legacyPermissions.forEach { (_, legacyPermission) ->
52             val permission =
53                 Permission(legacyPermission.permissionInfo, false, legacyPermission.type, 0)
54             permissions[permission.name] = permission
55             if (DEBUG_MIGRATION) {
56                 Slog.v(
57                     LOG_TAG,
58                     "Migrated permission: ${permission.name}, type: " +
59                         "${permission.type}, appId: ${permission.appId}, protectionLevel: " +
60                         "${permission.protectionLevel}, tree: $isPermissionTree"
61                 )
62             }
63         }
64     }
65 
66     internal fun migrateUserState(state: MutableAccessState, userId: Int) {
67         val permissionMigrationHelper =
68             LocalServices.getService(PermissionMigrationHelper::class.java)!!
69         if (!permissionMigrationHelper.hasLegacyPermissionState(userId)) {
70             return
71         }
72 
73         val legacyAppIdPermissionStates =
74             permissionMigrationHelper.getLegacyPermissionStates(userId)
75         val version = PackageVersionMigration.getVersion(userId)
76 
77         val userState = state.mutateUserState(userId)!!
78         val appIdPermissionFlags = userState.mutateAppIdPermissionFlags()
79         legacyAppIdPermissionStates.forEach { (appId, legacyPermissionStates) ->
80             val packageNames = state.externalState.appIdPackageNames[appId]
81             if (packageNames == null) {
82                 Slog.w(LOG_TAG, "Dropping unknown app ID $appId when migrating permission state")
83                 return@forEach
84             }
85 
86             val permissionFlags = MutableIndexedMap<String, Int>()
87             appIdPermissionFlags[appId] = permissionFlags
88             legacyPermissionStates.forEach forEachPermission@{
89                 (permissionName, legacyPermissionState) ->
90                 val permission = state.systemState.permissions[permissionName]
91                 if (permission == null) {
92                     Slog.w(
93                         LOG_TAG,
94                         "Dropping unknown permission $permissionName for app ID $appId" +
95                             " when migrating permission state"
96                     )
97                     return@forEachPermission
98                 }
99                 permissionFlags[permissionName] =
100                     migratePermissionFlags(permission, legacyPermissionState, appId, userId)
101             }
102 
103             val packageVersions = userState.mutatePackageVersions()
104             packageNames.forEachIndexed { _, packageName -> packageVersions[packageName] = version }
105         }
106     }
107 
108     private fun migratePermissionFlags(
109         permission: Permission,
110         legacyPermissionState: PermissionMigrationHelper.LegacyPermissionState,
111         appId: Int,
112         userId: Int
113     ): Int {
114         var flags =
115             when {
116                 permission.isNormal ->
117                     if (legacyPermissionState.isGranted) {
118                         PermissionFlags.INSTALL_GRANTED
119                     } else {
120                         PermissionFlags.INSTALL_REVOKED
121                     }
122                 permission.isSignature || permission.isInternal ->
123                     if (legacyPermissionState.isGranted) {
124                         if (permission.isDevelopment || permission.isRole) {
125                             PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED
126                         } else {
127                             PermissionFlags.PROTECTION_GRANTED
128                         }
129                     } else {
130                         0
131                     }
132                 permission.isRuntime ->
133                     if (legacyPermissionState.isGranted) PermissionFlags.RUNTIME_GRANTED else 0
134                 else -> 0
135             }
136         flags =
137             PermissionFlags.updateFlags(
138                 permission,
139                 flags,
140                 legacyPermissionState.flags,
141                 legacyPermissionState.flags
142             )
143         if (DEBUG_MIGRATION) {
144             val oldFlagString = PermissionFlags.apiFlagsToString(legacyPermissionState.flags)
145             val newFlagString = PermissionFlags.toString(flags)
146             val oldGrantState = legacyPermissionState.isGranted
147             val newGrantState = PermissionFlags.isPermissionGranted(flags)
148             val flagsMismatch = legacyPermissionState.flags != PermissionFlags.toApiFlags(flags)
149             Slog.v(
150                 LOG_TAG,
151                 "Migrated appId: $appId, permission: " +
152                     "${permission.name}, user: $userId, oldGrantState: $oldGrantState" +
153                     ", oldFlags: $oldFlagString, newFlags: $newFlagString, grantMismatch: " +
154                     "${oldGrantState != newGrantState}, flagsMismatch: $flagsMismatch"
155             )
156         }
157         return flags
158     }
159 
160     companion object {
161         private val LOG_TAG = AppIdPermissionMigration::class.java.simpleName
162 
163         private const val DEBUG_MIGRATION = false
164     }
165 }
166