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