Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1144ee01 authored by William Escande's avatar William Escande Committed by Gerrit Code Review
Browse files

Merge "CtsUtils: Split permissions in it's own object" into main

parents 91cc4a13 4e282105
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ java_library {
    srcs: [
        "src/**/*.java",
        "src/BlockingBluetoothAdapter.kt",
        "src/Permissions.kt",
    ],
    sdk_version: "test_current",
    visibility: [
+5 −33
Original line number Diff line number Diff line
@@ -17,13 +17,13 @@ package android.bluetooth.test_utils

import android.Manifest.permission.BLUETOOTH_CONNECT
import android.Manifest.permission.BLUETOOTH_PRIVILEGED
import android.app.UiAutomation
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothAdapter.ACTION_BLE_STATE_CHANGED
import android.bluetooth.BluetoothAdapter.STATE_BLE_ON
import android.bluetooth.BluetoothAdapter.STATE_OFF
import android.bluetooth.BluetoothAdapter.STATE_ON
import android.bluetooth.BluetoothManager
import android.bluetooth.test_utils.Permissions.withPermissions
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -53,7 +53,6 @@ object BlockingBluetoothAdapter {
    @JvmStatic val adapter = context.getSystemService(BluetoothManager::class.java).getAdapter()

    private val state = AdapterStateListener(context, adapter)
    private val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()

    // BLE_START_TIMEOUT_DELAY + BREDR_START_TIMEOUT_DELAY + (10 seconds of additional delay)
    private val stateChangeTimeout = 18.seconds
@@ -69,7 +68,7 @@ object BlockingBluetoothAdapter {
            throw IllegalStateException("Invalid call to enableBLE while current state is: $state")
        }
        Log.d(TAG, "Call to enableBLE")
        if (!withPermission(BLUETOOTH_CONNECT).use { adapter.enableBLE() }) {
        if (!withPermissions(BLUETOOTH_CONNECT).use { adapter.enableBLE() }) {
            Log.e(TAG, "enableBLE: Failed")
            return false
        }
@@ -83,7 +82,7 @@ object BlockingBluetoothAdapter {
            throw IllegalStateException("Invalid call to disableBLE while current state is: $state")
        }
        Log.d(TAG, "Call to disableBLE")
        if (!withPermission(BLUETOOTH_CONNECT).use { adapter.disableBLE() }) {
        if (!withPermissions(BLUETOOTH_CONNECT).use { adapter.disableBLE() }) {
            Log.e(TAG, "disableBLE: Failed")
            return false
        }
@@ -99,7 +98,7 @@ object BlockingBluetoothAdapter {
        }
        Log.d(TAG, "Call to enable")
        if (
            !withPermission(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED).use {
            !withPermissions(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED).use {
                @Suppress("DEPRECATION") adapter.enable()
            }
        ) {
@@ -118,7 +117,7 @@ object BlockingBluetoothAdapter {
        }
        Log.d(TAG, "Call to disable($persist)")
        if (
            !withPermission(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED).use {
            !withPermissions(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED).use {
                adapter.disable(persist)
            }
        ) {
@@ -129,33 +128,6 @@ object BlockingBluetoothAdapter {
        state.wasDisabled = true
        return state.waitForStateWithTimeout(stateChangeTimeout, STATE_OFF)
    }

    private fun restorePermissions(permissions: Set<String>) {
        if (UiAutomation.ALL_PERMISSIONS.equals(permissions)) {
            uiAutomation.adoptShellPermissionIdentity()
        } else {
            uiAutomation.adoptShellPermissionIdentity(*permissions.map { it }.toTypedArray())
        }
    }

    private fun replacePermissionsWith(vararg newPermissions: String): Set<String> {
        val currentPermissions = uiAutomation.getAdoptedShellPermissions()
        if (newPermissions.size == 0) {
            // Throw even if the code support it as we are not expecting this by design
            throw IllegalArgumentException("Invalid permissions replacement with no permissions.")
        }
        uiAutomation.adoptShellPermissionIdentity(*newPermissions)
        return currentPermissions
    }

    // Set permissions to be used as long as the resource is open.
    // Restore initial permissions after closing resource.
    private fun withPermission(
        vararg newPermissions: String,
    ): AutoCloseable {
        val savedPermissions = replacePermissionsWith(*newPermissions)
        return AutoCloseable { restorePermissions(savedPermissions) }
    }
}

private class AdapterStateListener(context: Context, private val adapter: BluetoothAdapter) {
+83 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.bluetooth.test_utils

import android.app.UiAutomation
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertThrows

private const val TAG: String = "Permissions"

object Permissions {
    private val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()

    public interface PermissionContext : AutoCloseable {
        // Override AutoCloseable method to silent the requirement on Exception
        override fun close()
    }

    /**
     * Set permissions to be used as long as the resource is open. Restore initial permissions after
     * closing resource.
     *
     * @param newPermissions Permissions to hold when using resource. You need to specify at least 1
     */
    @JvmStatic
    fun withPermissions(vararg newPermissions: String): PermissionContext {
        val savedPermissions = replacePermissionsWith(*newPermissions)
        return object : PermissionContext {
            override fun close() {
                restorePermissions(savedPermissions)
            }
        }
    }

    @JvmStatic
    fun enforceEachPermissions(action: () -> Any, newPermissions: List<String>) {
        if (newPermissions.size < 2) {
            throw IllegalArgumentException("Not supported for less than 2 permissions")
        }
        newPermissions.forEach {
            val permissionsSet = newPermissions.toMutableSet()
            permissionsSet.remove(it)

            withPermissions(*arrayOf(*permissionsSet.toTypedArray())).use {
                assertThrows(SecurityException::class.java, { action() })
            }
        }
    }

    private fun restorePermissions(permissions: Set<String>) {
        if (UiAutomation.ALL_PERMISSIONS.equals(permissions)) {
            uiAutomation.adoptShellPermissionIdentity()
        } else {
            uiAutomation.adoptShellPermissionIdentity(*permissions.map { it }.toTypedArray())
        }
        Log.d(TAG, "Restored ${permissions}")
    }

    private fun replacePermissionsWith(vararg newPermissions: String): Set<String> {
        val currentPermissions = uiAutomation.getAdoptedShellPermissions()
        if (newPermissions.size == 0) {
            // Throw even if the code support it as we are not expecting this by design
            throw IllegalArgumentException("Invalid permissions replacement with no permissions.")
        }
        uiAutomation.adoptShellPermissionIdentity(*newPermissions)
        Log.d(TAG, "Replaced ${currentPermissions} with ${newPermissions.toSet()}")
        return currentPermissions
    }
}