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.test
18 
19 import android.Manifest
20 import android.content.pm.PackageManager
21 import android.content.pm.PermissionGroupInfo
22 import android.content.pm.PermissionInfo
23 import android.content.pm.SigningDetails
24 import android.os.Build
25 import android.os.Bundle
26 import android.util.ArrayMap
27 import android.util.SparseArray
28 import androidx.test.ext.junit.runners.AndroidJUnit4
29 import com.android.internal.pm.pkg.component.ParsedPermission
30 import com.android.internal.pm.pkg.component.ParsedPermissionGroup
31 import com.android.modules.utils.testing.ExtendedMockitoRule
32 import com.android.server.extendedtestutils.wheneverStatic
33 import com.android.server.permission.access.MutableAccessState
34 import com.android.server.permission.access.MutableUserState
35 import com.android.server.permission.access.MutateStateScope
36 import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
37 import com.android.server.permission.access.permission.AppIdPermissionPolicy
38 import com.android.server.permission.access.permission.Permission
39 import com.android.server.permission.access.util.hasBits
40 import com.android.server.pm.parsing.PackageInfoUtils
41 import com.android.server.pm.pkg.AndroidPackage
42 import com.android.server.pm.pkg.PackageState
43 import com.android.server.pm.pkg.PackageUserState
44 import com.android.server.testutils.any
45 import com.android.server.testutils.mock
46 import com.android.server.testutils.whenever
47 import org.junit.Before
48 import org.junit.Rule
49 import org.junit.runner.RunWith
50 import org.mockito.ArgumentMatchers.anyLong
51 
52 /**
53  * Mocking unit test for AppIdPermissionPolicy.
54  */
55 @RunWith(AndroidJUnit4::class)
56 abstract class BasePermissionPolicyTest {
57     protected lateinit var oldState: MutableAccessState
58     protected lateinit var newState: MutableAccessState
59 
60     protected val defaultPermissionGroup = mockParsedPermissionGroup(
61         PERMISSION_GROUP_NAME_0,
62         PACKAGE_NAME_0
63     )
64     protected val defaultPermissionTree = mockParsedPermission(
65         PERMISSION_TREE_NAME,
66         PACKAGE_NAME_0,
67         isTree = true
68     )
69     protected val defaultPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
70 
71     protected val appIdPermissionPolicy = AppIdPermissionPolicy()
72 
73     @Rule
74     @JvmField
75     val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
76         .spyStatic(PackageInfoUtils::class.java)
77         .build()
78 
79     @Before
80     fun baseSetUp() {
81         oldState = MutableAccessState()
82         createUserState(USER_ID_0)
83         oldState.mutateExternalState().setPackageStates(ArrayMap())
84         oldState.mutateExternalState().setDisabledSystemPackageStates(ArrayMap())
85         mockPackageInfoUtilsGeneratePermissionInfo()
86         mockPackageInfoUtilsGeneratePermissionGroupInfo()
87     }
88 
89     protected fun createUserState(userId: Int) {
90         oldState.mutateExternalState().mutateUserIds().add(userId)
91         oldState.mutateUserStatesNoWrite().put(userId, MutableUserState())
92     }
93 
94     private fun mockPackageInfoUtilsGeneratePermissionInfo() {
95         wheneverStatic {
96             PackageInfoUtils.generatePermissionInfo(any(ParsedPermission::class.java), anyLong())
97         }.thenAnswer { invocation ->
98             val parsedPermission = invocation.getArgument<ParsedPermission>(0)
99             val generateFlags = invocation.getArgument<Long>(1)
100             PermissionInfo(parsedPermission.backgroundPermission).apply {
101                 name = parsedPermission.name
102                 packageName = parsedPermission.packageName
103                 metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
104                     parsedPermission.metaData
105                 } else {
106                     null
107                 }
108                 @Suppress("DEPRECATION")
109                 protectionLevel = parsedPermission.protectionLevel
110                 group = parsedPermission.group
111                 flags = parsedPermission.flags
112             }
113         }
114     }
115 
116     private fun mockPackageInfoUtilsGeneratePermissionGroupInfo() {
117         wheneverStatic {
118             PackageInfoUtils.generatePermissionGroupInfo(
119                 any(ParsedPermissionGroup::class.java),
120                 anyLong()
121             )
122         }.thenAnswer { invocation ->
123             val parsedPermissionGroup = invocation.getArgument<ParsedPermissionGroup>(0)
124             val generateFlags = invocation.getArgument<Long>(1)
125             @Suppress("DEPRECATION")
126             PermissionGroupInfo().apply {
127                 name = parsedPermissionGroup.name
128                 packageName = parsedPermissionGroup.packageName
129                 metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
130                     parsedPermissionGroup.metaData
131                 } else {
132                     null
133                 }
134                 flags = parsedPermissionGroup.flags
135             }
136         }
137     }
138 
139     /**
140      * Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0
141      */
142     protected fun mockSimpleAndroidPackage(): AndroidPackage =
143         mockAndroidPackage(
144             PACKAGE_NAME_0,
145             permissionGroups = listOf(defaultPermissionGroup),
146             permissions = listOf(defaultPermissionTree, defaultPermission)
147         )
148 
149     protected fun createSimplePermission(isTree: Boolean = false): Permission {
150         val parsedPermission = if (isTree) { defaultPermissionTree } else { defaultPermission }
151         val permissionInfo = PackageInfoUtils.generatePermissionInfo(
152             parsedPermission,
153             PackageManager.GET_META_DATA.toLong()
154         )!!
155         return Permission(permissionInfo, true, Permission.TYPE_MANIFEST, APP_ID_0)
156     }
157 
158     protected inline fun mutateState(action: MutateStateScope.() -> Unit) {
159         newState = oldState.toMutable()
160         MutateStateScope(oldState, newState).action()
161     }
162 
163     protected fun mockPackageState(
164         appId: Int,
165         packageName: String,
166         isSystem: Boolean = false,
167     ): PackageState =
168         mock {
169             whenever(this.appId).thenReturn(appId)
170             whenever(this.packageName).thenReturn(packageName)
171             whenever(androidPackage).thenReturn(null)
172             whenever(this.isSystem).thenReturn(isSystem)
173         }
174 
175     protected fun mockPackageState(
176         appId: Int,
177         androidPackage: AndroidPackage,
178         isSystem: Boolean = false,
179         isPrivileged: Boolean = false,
180         isProduct: Boolean = false,
181         isInstantApp: Boolean = false,
182         isVendor: Boolean = false
183     ): PackageState =
184         mock {
185             whenever(this.appId).thenReturn(appId)
186             whenever(this.androidPackage).thenReturn(androidPackage)
187             val packageName = androidPackage.packageName
188             whenever(this.packageName).thenReturn(packageName)
189             whenever(this.isSystem).thenReturn(isSystem)
190             whenever(this.isPrivileged).thenReturn(isPrivileged)
191             whenever(this.isProduct).thenReturn(isProduct)
192             whenever(this.isVendor).thenReturn(isVendor)
193             val userStates = SparseArray<PackageUserState>().apply {
194                 put(USER_ID_0, mock { whenever(this.isInstantApp).thenReturn(isInstantApp) })
195             }
196             whenever(this.userStates).thenReturn(userStates)
197         }
198 
199     protected fun mockAndroidPackage(
200         packageName: String,
201         targetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
202         isRequestLegacyExternalStorage: Boolean = false,
203         adoptPermissions: List<String> = emptyList(),
204         implicitPermissions: Set<String> = emptySet(),
205         requestedPermissions: Set<String> = emptySet(),
206         permissionGroups: List<ParsedPermissionGroup> = emptyList(),
207         permissions: List<ParsedPermission> = emptyList(),
208         isSignatureMatching: Boolean = false
209     ): AndroidPackage =
210         mock {
211             whenever(this.packageName).thenReturn(packageName)
212             whenever(this.targetSdkVersion).thenReturn(targetSdkVersion)
213             whenever(this.isRequestLegacyExternalStorage).thenReturn(isRequestLegacyExternalStorage)
214             whenever(this.adoptPermissions).thenReturn(adoptPermissions)
215             whenever(this.implicitPermissions).thenReturn(implicitPermissions)
216             whenever(this.requestedPermissions).thenReturn(requestedPermissions)
217             whenever(this.permissionGroups).thenReturn(permissionGroups)
218             whenever(this.permissions).thenReturn(permissions)
219             val signingDetails = mock<SigningDetails> {
220                 whenever(
221                     hasCommonSignerWithCapability(any(), any())
222                 ).thenReturn(isSignatureMatching)
223                 whenever(hasAncestorOrSelf(any())).thenReturn(isSignatureMatching)
224                 whenever(
225                     checkCapability(any<SigningDetails>(), any())
226                 ).thenReturn(isSignatureMatching)
227             }
228             whenever(this.signingDetails).thenReturn(signingDetails)
229         }
230 
231     protected fun mockParsedPermission(
232         permissionName: String,
233         packageName: String,
234         backgroundPermission: String? = null,
235         group: String? = null,
236         protectionLevel: Int = PermissionInfo.PROTECTION_NORMAL,
237         flags: Int = 0,
238         isTree: Boolean = false
239     ): ParsedPermission =
240         mock {
241             whenever(name).thenReturn(permissionName)
242             whenever(this.packageName).thenReturn(packageName)
243             whenever(metaData).thenReturn(Bundle())
244             whenever(this.backgroundPermission).thenReturn(backgroundPermission)
245             whenever(this.group).thenReturn(group)
246             whenever(this.protectionLevel).thenReturn(protectionLevel)
247             whenever(this.flags).thenReturn(flags)
248             whenever(this.isTree).thenReturn(isTree)
249         }
250 
251     protected fun mockParsedPermissionGroup(
252         permissionGroupName: String,
253         packageName: String,
254     ): ParsedPermissionGroup =
255         mock {
256             whenever(name).thenReturn(permissionGroupName)
257             whenever(this.packageName).thenReturn(packageName)
258             whenever(metaData).thenReturn(Bundle())
259         }
260 
261     protected fun addPackageState(
262         packageState: PackageState,
263         state: MutableAccessState = oldState
264     ) {
265         state.mutateExternalState().apply {
266             setPackageStates(
267                 packageStates.toMutableMap().apply { put(packageState.packageName, packageState) }
268             )
269             mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
270                 .add(packageState.packageName)
271         }
272     }
273 
274     protected fun removePackageState(
275         packageState: PackageState,
276         state: MutableAccessState = oldState
277     ) {
278         state.mutateExternalState().apply {
279             setPackageStates(
280                 packageStates.toMutableMap().apply { remove(packageState.packageName) }
281             )
282             mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
283                 .remove(packageState.packageName)
284         }
285     }
286 
287     protected fun addDisabledSystemPackageState(
288         packageState: PackageState,
289         state: MutableAccessState = oldState
290     ) = state.mutateExternalState().apply {
291         (disabledSystemPackageStates as ArrayMap)[packageState.packageName] = packageState
292     }
293 
294     protected fun addPermission(
295         parsedPermission: ParsedPermission,
296         type: Int = Permission.TYPE_MANIFEST,
297         isReconciled: Boolean = true,
298         state: MutableAccessState = oldState
299     ) {
300         val permissionInfo = PackageInfoUtils.generatePermissionInfo(
301             parsedPermission,
302             PackageManager.GET_META_DATA.toLong()
303         )!!
304         val appId = state.externalState.packageStates[permissionInfo.packageName]!!.appId
305         val permission = Permission(permissionInfo, isReconciled, type, appId)
306         if (parsedPermission.isTree) {
307             state.mutateSystemState().mutatePermissionTrees()[permission.name] = permission
308         } else {
309             state.mutateSystemState().mutatePermissions()[permission.name] = permission
310         }
311     }
312 
313     protected fun addPermissionGroup(
314         parsedPermissionGroup: ParsedPermissionGroup,
315         state: MutableAccessState = oldState
316     ) {
317         state.mutateSystemState().mutatePermissionGroups()[parsedPermissionGroup.name] =
318             PackageInfoUtils.generatePermissionGroupInfo(
319                 parsedPermissionGroup,
320                 PackageManager.GET_META_DATA.toLong()
321             )!!
322     }
323 
324     protected fun getPermission(
325         permissionName: String,
326         state: MutableAccessState = newState
327     ): Permission? = state.systemState.permissions[permissionName]
328 
329     protected fun getPermissionTree(
330         permissionTreeName: String,
331         state: MutableAccessState = newState
332     ): Permission? = state.systemState.permissionTrees[permissionTreeName]
333 
334     protected fun getPermissionGroup(
335         permissionGroupName: String,
336         state: MutableAccessState = newState
337     ): PermissionGroupInfo? = state.systemState.permissionGroups[permissionGroupName]
338 
339     protected fun getPermissionFlags(
340         appId: Int,
341         userId: Int,
342         permissionName: String,
343         state: MutableAccessState = newState
344     ): Int =
345         state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0)
346 
347     protected fun setPermissionFlags(
348         appId: Int,
349         userId: Int,
350         permissionName: String,
351         flags: Int,
352         state: MutableAccessState = oldState
353     ) =
354         state.mutateUserState(userId)!!.mutateAppIdPermissionFlags().mutateOrPut(appId) {
355             MutableIndexedMap()
356         }.put(permissionName, flags)
357 
358     companion object {
359         @JvmStatic protected val PACKAGE_NAME_0 = "packageName0"
360         @JvmStatic protected val PACKAGE_NAME_1 = "packageName1"
361         @JvmStatic protected val PACKAGE_NAME_2 = "packageName2"
362         @JvmStatic protected val MISSING_ANDROID_PACKAGE = "missingAndroidPackage"
363         @JvmStatic protected val PLATFORM_PACKAGE_NAME = "android"
364 
365         @JvmStatic protected val APP_ID_0 = 0
366         @JvmStatic protected val APP_ID_1 = 1
367         @JvmStatic protected val PLATFORM_APP_ID = 2
368 
369         @JvmStatic protected val PERMISSION_GROUP_NAME_0 = "permissionGroupName0"
370         @JvmStatic protected val PERMISSION_GROUP_NAME_1 = "permissionGroupName1"
371 
372         @JvmStatic protected val PERMISSION_TREE_NAME = "permissionTree"
373 
374         @JvmStatic protected val PERMISSION_NAME_0 = "permissionName0"
375         @JvmStatic protected val PERMISSION_NAME_1 = "permissionName1"
376         @JvmStatic protected val PERMISSION_NAME_2 = "permissionName2"
377         @JvmStatic protected val PERMISSION_BELONGS_TO_A_TREE = "permissionTree.permission"
378         @JvmStatic protected val PERMISSION_READ_EXTERNAL_STORAGE =
379             Manifest.permission.READ_EXTERNAL_STORAGE
380         @JvmStatic protected val PERMISSION_POST_NOTIFICATIONS =
381             Manifest.permission.POST_NOTIFICATIONS
382         @JvmStatic protected val PERMISSION_BLUETOOTH_CONNECT =
383             Manifest.permission.BLUETOOTH_CONNECT
384         @JvmStatic protected val PERMISSION_ACCESS_BACKGROUND_LOCATION =
385             Manifest.permission.ACCESS_BACKGROUND_LOCATION
386         @JvmStatic protected val PERMISSION_ACCESS_MEDIA_LOCATION =
387             Manifest.permission.ACCESS_MEDIA_LOCATION
388 
389         @JvmStatic protected val USER_ID_0 = 0
390         @JvmStatic protected val USER_ID_NEW = 1
391     }
392 }
393