1 /* 2 * Copyright (C) 2022 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.cts.verifier.bluetooth; 18 19 import static android.content.Context.RECEIVER_EXPORTED; 20 21 import static com.android.compatibility.common.util.ShellIdentityUtils.invokeWithShellPermissions; 22 23 import android.bluetooth.BluetoothAdapter; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.os.Handler; 29 import android.os.HandlerThread; 30 import android.os.Looper; 31 import android.util.Log; 32 33 import java.util.concurrent.TimeUnit; 34 import java.util.concurrent.locks.Condition; 35 import java.util.concurrent.locks.ReentrantLock; 36 37 /** 38 * Utility for controlling the Bluetooth adapter from CTS test. 39 * 40 * Code mostly copied from android.bluetooth.cts.BTAdapterUtils class. 41 */ 42 public class BtAdapterUtils { 43 private static final String TAG = "BtAdapterUtils"; 44 45 // ADAPTER_ENABLE_TIMEOUT_MS = AdapterState.BLE_START_TIMEOUT_DELAY + 46 // AdapterState.BREDR_START_TIMEOUT_DELAY 47 private static final int ADAPTER_ENABLE_TIMEOUT_MS = 8000; 48 // ADAPTER_DISABLE_TIMEOUT_MS = AdapterState.BLE_STOP_TIMEOUT_DELAY + 49 // AdapterState.BREDR_STOP_TIMEOUT_DELAY 50 private static final int ADAPTER_DISABLE_TIMEOUT_MS = 5000; 51 52 private static BroadcastReceiver sAdapterIntentReceiver; 53 54 private static Condition sConditionAdapterIsEnabled; 55 private static ReentrantLock sAdapterStateEnablingLock; 56 57 private static Condition sConditionAdapterIsDisabled; 58 private static ReentrantLock sAdapterStateDisablingLock; 59 private static boolean sAdapterVarsInitialized; 60 61 private static HandlerThread sHandlerThread; 62 private static Looper sLooper; 63 private static Handler sHandler; 64 65 private static class AdapterIntentReceiver extends BroadcastReceiver { 66 @Override onReceive(Context context, Intent intent)67 public void onReceive(Context context, Intent intent) { 68 if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { 69 int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1); 70 int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); 71 Log.d(TAG, "Previous state: " + previousState + " New state: " + newState); 72 73 if (newState == BluetoothAdapter.STATE_ON) { 74 sAdapterStateEnablingLock.lock(); 75 try { 76 Log.d(TAG, "Signaling to mConditionAdapterIsEnabled"); 77 sConditionAdapterIsEnabled.signal(); 78 } finally { 79 sAdapterStateEnablingLock.unlock(); 80 } 81 } else if (newState == BluetoothAdapter.STATE_OFF) { 82 sAdapterStateDisablingLock.lock(); 83 try { 84 Log.d(TAG, "Signaling to mConditionAdapterIsDisabled"); 85 sConditionAdapterIsDisabled.signal(); 86 } finally { 87 sAdapterStateDisablingLock.unlock(); 88 } 89 } 90 } 91 } 92 } 93 94 /** Enables the Bluetooth Adapter. Return true if it is already enabled or is enabled. */ enableAdapter(BluetoothAdapter bluetoothAdapter, Context context)95 public static boolean enableAdapter(BluetoothAdapter bluetoothAdapter, Context context) { 96 if (!sAdapterVarsInitialized) { 97 initAdapterStateVariables(context); 98 } 99 registerIntentReceiver(context); 100 101 if (bluetoothAdapter.isEnabled()) return true; 102 103 invokeWithShellPermissions(() -> bluetoothAdapter.enable()); 104 sAdapterStateEnablingLock.lock(); 105 try { 106 // Wait for the Adapter to be enabled 107 while (!bluetoothAdapter.isEnabled()) { 108 if (!sConditionAdapterIsEnabled.await( 109 ADAPTER_ENABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 110 // Timeout 111 Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter enable"); 112 break; 113 } // else spurious wakeups 114 } 115 } catch (InterruptedException e) { 116 Log.e(TAG, "enableAdapter: interrupted"); 117 } finally { 118 sAdapterStateEnablingLock.unlock(); 119 } 120 return bluetoothAdapter.isEnabled(); 121 } 122 123 /** Disable the Bluetooth Adapter. Return true if it is already disabled or is disabled. */ disableAdapter(BluetoothAdapter bluetoothAdapter, Context context)124 public static boolean disableAdapter(BluetoothAdapter bluetoothAdapter, Context context) { 125 if (!sAdapterVarsInitialized) { 126 initAdapterStateVariables(context); 127 } 128 registerIntentReceiver(context); 129 130 if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) return true; 131 132 invokeWithShellPermissions(() -> bluetoothAdapter.disable()); 133 sAdapterStateDisablingLock.lock(); 134 try { 135 // Wait for the Adapter to be disabled 136 while (bluetoothAdapter.getState() != BluetoothAdapter.STATE_OFF) { 137 if (!sConditionAdapterIsDisabled.await( 138 ADAPTER_DISABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 139 // Timeout 140 Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter disable"); 141 break; 142 } // else spurious wakeups 143 } 144 } catch (InterruptedException e) { 145 Log.e(TAG, "disableAdapter: interrupted"); 146 } finally { 147 sAdapterStateDisablingLock.unlock(); 148 } 149 return bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF; 150 } 151 registerIntentReceiver(Context context)152 private static void registerIntentReceiver(Context context) { 153 sAdapterIntentReceiver = new AdapterIntentReceiver(); 154 IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); 155 context.registerReceiver(sAdapterIntentReceiver, filter, RECEIVER_EXPORTED); 156 } 157 158 // Initialize variables required for TestUtils#enableAdapter and TestUtils#disableAdapter initAdapterStateVariables(Context context)159 private static void initAdapterStateVariables(Context context) { 160 sAdapterStateEnablingLock = new ReentrantLock(); 161 sConditionAdapterIsEnabled = sAdapterStateEnablingLock.newCondition(); 162 sAdapterStateDisablingLock = new ReentrantLock(); 163 sConditionAdapterIsDisabled = sAdapterStateDisablingLock.newCondition(); 164 165 sAdapterVarsInitialized = true; 166 } 167 } 168