1 /*
2  * Copyright (C) 2024 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 package android.bluetooth.test_utils
17 
18 import android.app.UiAutomation
19 import android.util.Log
20 import androidx.test.platform.app.InstrumentationRegistry
21 import org.junit.Assert.assertThrows
22 
23 private const val TAG: String = "Permissions"
24 
25 object Permissions {
26     private val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
27 
28     public interface PermissionContext : AutoCloseable {
29         // Override AutoCloseable method to silent the requirement on Exception
closenull30         override fun close()
31     }
32 
33     /**
34      * Set permissions to be used as long as the resource is open. Restore initial permissions after
35      * closing resource.
36      *
37      * @param newPermissions Permissions to hold when using resource. You need to specify at least 1
38      */
39     @JvmStatic
40     fun withPermissions(vararg newPermissions: String): PermissionContext {
41         val savedPermissions = replacePermissionsWith(*newPermissions)
42         return object : PermissionContext {
43             override fun close() {
44                 restorePermissions(savedPermissions)
45             }
46         }
47     }
48 
49     @JvmStatic
enforceEachPermissionsnull50     fun enforceEachPermissions(action: () -> Any, newPermissions: List<String>) {
51         if (newPermissions.size < 2) {
52             throw IllegalArgumentException("Not supported for less than 2 permissions")
53         }
54         newPermissions.forEach {
55             val permissionsSet = newPermissions.toMutableSet()
56             permissionsSet.remove(it)
57 
58             withPermissions(*arrayOf(*permissionsSet.toTypedArray())).use {
59                 assertThrows(SecurityException::class.java, { action() })
60             }
61         }
62     }
63 
restorePermissionsnull64     private fun restorePermissions(permissions: Set<String>) {
65         if (UiAutomation.ALL_PERMISSIONS.equals(permissions)) {
66             uiAutomation.adoptShellPermissionIdentity()
67         } else if (permissions.size == 0) {
68             uiAutomation.dropShellPermissionIdentity()
69         } else {
70             uiAutomation.adoptShellPermissionIdentity(*permissions.map { it }.toTypedArray())
71         }
72         Log.d(TAG, "Restored ${permissions}")
73     }
74 
replacePermissionsWithnull75     private fun replacePermissionsWith(vararg newPermissions: String): Set<String> {
76         val currentPermissions = uiAutomation.getAdoptedShellPermissions()
77         if (newPermissions.size == 0) {
78             // Throw even if the code support it as we are not expecting this by design
79             throw IllegalArgumentException("Invalid permissions replacement with no permissions.")
80         }
81         uiAutomation.adoptShellPermissionIdentity(*newPermissions)
82         Log.d(TAG, "Replaced ${currentPermissions} with ${newPermissions.toSet()}")
83         return currentPermissions
84     }
85 }
86