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 {
68             uiAutomation.adoptShellPermissionIdentity(*permissions.map { it }.toTypedArray())
69         }
70         Log.d(TAG, "Restored ${permissions}")
71     }
72 
replacePermissionsWithnull73     private fun replacePermissionsWith(vararg newPermissions: String): Set<String> {
74         val currentPermissions = uiAutomation.getAdoptedShellPermissions()
75         if (newPermissions.size == 0) {
76             // Throw even if the code support it as we are not expecting this by design
77             throw IllegalArgumentException("Invalid permissions replacement with no permissions.")
78         }
79         uiAutomation.adoptShellPermissionIdentity(*newPermissions)
80         Log.d(TAG, "Replaced ${currentPermissions} with ${newPermissions.toSet()}")
81         return currentPermissions
82     }
83 }
84