1 /* 2 * Copyright (C) 2017 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.googlecode.android_scripting.facade.bluetooth; 18 19 import android.app.Service; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothHeadset; 23 import android.bluetooth.BluetoothProfile; 24 import android.bluetooth.BluetoothUuid; 25 import android.os.ParcelUuid; 26 27 import com.googlecode.android_scripting.Log; 28 import com.googlecode.android_scripting.facade.FacadeManager; 29 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 30 import com.googlecode.android_scripting.rpc.Rpc; 31 import com.googlecode.android_scripting.rpc.RpcParameter; 32 33 import java.util.List; 34 35 public class BluetoothHspFacade extends RpcReceiver { 36 static final ParcelUuid[] UUIDS = { 37 BluetoothUuid.HSP, BluetoothUuid.Handsfree 38 }; 39 40 private final Service mService; 41 private final BluetoothAdapter mBluetoothAdapter; 42 43 private static boolean sIsHspReady = false; 44 private static BluetoothHeadset sHspProfile = null; 45 46 public BluetoothHspFacade(FacadeManager manager) { 47 super(manager); 48 mService = manager.getService(); 49 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 50 mBluetoothAdapter.getProfileProxy(mService, new HspServiceListener(), 51 BluetoothProfile.HEADSET); 52 } 53 54 class HspServiceListener implements BluetoothProfile.ServiceListener { 55 @Override 56 public void onServiceConnected(int profile, BluetoothProfile proxy) { 57 sHspProfile = (BluetoothHeadset) proxy; 58 sIsHspReady = true; 59 } 60 61 @Override 62 public void onServiceDisconnected(int profile) { 63 sIsHspReady = false; 64 } 65 } 66 67 /** 68 * Connect to Hsp Profile 69 * @param device - the BluetoothDevice object to connect to. 70 * @return if the connection was successfull or not. 71 */ 72 public Boolean hspConnect(BluetoothDevice device) { 73 if (sHspProfile == null) return false; 74 return sHspProfile.connect(device); 75 } 76 77 /** 78 * Disconnect to Hsp Profile. 79 * @param device - the Bluetooth Device object to disconnect from. 80 * @return if the disconnection was successfull or not. 81 */ 82 public Boolean hspDisconnect(BluetoothDevice device) { 83 if (sHspProfile == null) return false; 84 return sHspProfile.disconnect(device); 85 } 86 87 /** 88 * Wait Hsp Profile ready until timeout. 89 * @param timeout - Timeout second. 90 * @return true if Hsp Profile is ready else false. 91 */ 92 public Boolean waitHspReady(long timeout) { 93 long startTime = System.currentTimeMillis(); 94 while (System.currentTimeMillis() < startTime + timeout * 1000) { 95 if (sIsHspReady) return true; 96 } 97 Log.d("Hsp profile is not ready"); 98 return false; 99 } 100 101 /** 102 * Is Hsp profile ready. 103 * @return if Hsp profile is ready or not. 104 */ 105 @Rpc(description = "Is Hsp profile ready.") 106 public Boolean bluetoothHspIsReady() { 107 return sIsHspReady; 108 } 109 110 /** 111 * Set priority of the profile. 112 * @param deviceStr - name or MAC address of a Bluetooth device. 113 * @param priority - Priority that needs to be set. 114 */ 115 @Rpc(description = "Set priority of the profile.") 116 public void bluetoothHspSetPriority( 117 @RpcParameter(name = "device", description = "Mac address of a BT device.") 118 String deviceStr, 119 @RpcParameter(name = "priority", description = "Priority that needs to be set.") 120 Integer priority) throws Exception { 121 if (!waitHspReady(10)) return; 122 BluetoothDevice device = BluetoothFacade.getDevice( 123 mBluetoothAdapter.getBondedDevices(), deviceStr); 124 Log.d("Changing priority of device " + device.getAliasName() + " p: " + priority); 125 sHspProfile.setPriority(device, priority); 126 } 127 128 /** 129 * Connect to an HSP device. 130 * @param device - Name or MAC address of a bluetooth device. 131 * @return True if the connection was successful; otherwise False. 132 */ 133 @Rpc(description = "Connect to an HSP device.") 134 public Boolean bluetoothHspConnect( 135 @RpcParameter(name = "device", description = 136 "Name or MAC address of a bluetooth device.") 137 String device) throws Exception { 138 if (!waitHspReady(10)) return false; 139 BluetoothDevice mDevice = BluetoothFacade.getDevice( 140 mBluetoothAdapter.getBondedDevices(), device); 141 Log.d("Connecting to device " + mDevice.getAliasName()); 142 return hspConnect(mDevice); 143 } 144 145 /** 146 * Disconnect an HSP device. 147 * @param device - Name or MAC address of a bluetooth device. 148 * @return True if the disconnection was successful; otherwise False. 149 */ 150 @Rpc(description = "Disconnect an HSP device.") 151 public Boolean bluetoothHspDisconnect( 152 @RpcParameter(name = "device", description = "Name or MAC address of a device.") 153 String device) throws Exception { 154 if (!waitHspReady(10)) return false; 155 Log.d("Connected devices: " + sHspProfile.getConnectedDevices()); 156 BluetoothDevice mDevice = BluetoothFacade.getDevice( 157 sHspProfile.getConnectedDevices(), device); 158 return hspDisconnect(mDevice); 159 } 160 161 /** 162 * Get all the devices connected through HSP. 163 * @return List of all the devices connected through HSP. 164 */ 165 @Rpc(description = "Get all the devices connected through HSP.") 166 public List<BluetoothDevice> bluetoothHspGetConnectedDevices() { 167 if (!waitHspReady(10)) return null; 168 return sHspProfile.getConnectedDevices(); 169 } 170 171 /** 172 * Get the connection status of a device. 173 * @param deviceID - Name or MAC address of a bluetooth device. 174 * @return connection status of a device. 175 */ 176 @Rpc(description = "Get the connection status of a device.") 177 public Integer bluetoothHspGetConnectionStatus( 178 @RpcParameter(name = "deviceID", 179 description = "Name or MAC address of a bluetooth device.") 180 String deviceID) { 181 if (!waitHspReady(10)) { 182 return BluetoothProfile.STATE_DISCONNECTED; 183 } 184 List<BluetoothDevice> deviceList = sHspProfile.getConnectedDevices(); 185 BluetoothDevice device; 186 try { 187 device = BluetoothFacade.getDevice(deviceList, deviceID); 188 } catch (Exception e) { 189 return BluetoothProfile.STATE_DISCONNECTED; 190 } 191 return sHspProfile.getConnectionState(device); 192 } 193 194 /** 195 * Force SCO audio on DUT, ignore all other restrictions 196 * 197 * @param force True to force SCO audio, False to resume normal 198 * @return True if the setup is successful 199 */ 200 @Rpc(description = "Force SCO audio connection on DUT.") 201 public Boolean bluetoothHspForceScoAudio( 202 @RpcParameter(name = "force", description = "whether to force SCO audio") 203 Boolean force) { 204 if (!waitHspReady(10)) { 205 return false; 206 } 207 sHspProfile.setForceScoAudio(force); 208 return true; 209 } 210 211 /** 212 * Connect SCO audio to a remote device 213 * 214 * @param deviceAddress the Bluetooth MAC address of remote device 215 * @return True if connection is successful, False otherwise 216 */ 217 @Rpc(description = "Connect SCO audio for a remote device.") 218 public Boolean bluetoothHspConnectAudio( 219 @RpcParameter(name = "deviceAddress", 220 description = "MAC address of a bluetooth device.") 221 String deviceAddress) { 222 if (!waitHspReady(10)) { 223 return false; 224 } 225 Log.d("Connected devices: " + sHspProfile.getConnectedDevices()); 226 BluetoothDevice device = null; 227 if (sHspProfile.getConnectedDevices().size() > 1) { 228 Log.d("More than one device available"); 229 } 230 try { 231 device = BluetoothFacade.getDevice( 232 sHspProfile.getConnectedDevices(), deviceAddress); 233 } catch (Exception e) { 234 Log.d("Cannot find device " + deviceAddress); 235 return false; 236 } 237 return sHspProfile.connectAudio(); 238 } 239 240 /** 241 * Disconnect SCO audio for a remote device 242 * 243 * @param deviceAddress the Bluetooth MAC address of remote device 244 * @return True if disconnection is successful, False otherwise 245 */ 246 @Rpc(description = "Disconnect SCO audio for a remote device") 247 public Boolean bluetoothHspDisconnectAudio( 248 @RpcParameter(name = "deviceAddress", 249 description = "MAC address of a bluetooth device.") 250 String deviceAddress) { 251 if (!waitHspReady(10)) { 252 return false; 253 } 254 Log.d("Connected devices: " + sHspProfile.getConnectedDevices()); 255 BluetoothDevice device = null; 256 if (sHspProfile.getConnectedDevices().size() > 1) { 257 Log.d("More than one device available"); 258 } 259 try { 260 device = BluetoothFacade.getDevice( 261 sHspProfile.getConnectedDevices(), deviceAddress); 262 } catch (Exception e) { 263 Log.d("Cannot find device " + deviceAddress); 264 return false; 265 } 266 if (!sHspProfile.isAudioConnected(device)) { 267 Log.d("SCO audio is not connected for device " + deviceAddress); 268 return false; 269 } 270 return sHspProfile.disconnectAudio(); 271 } 272 273 /** 274 * Check if SCO audio is connected for a remote device 275 * 276 * @param deviceAddress the Bluetooth MAC address of remote device 277 * @return True if device is connected to us via SCO audio, False otherwise 278 */ 279 @Rpc(description = "Check if SCO audio is connected for a remote device") 280 public Boolean bluetoothHspIsAudioConnected( 281 @RpcParameter(name = "deviceAddress", 282 description = "MAC address of a bluetooth device.") 283 String deviceAddress) { 284 if (!waitHspReady(10)) { 285 return false; 286 } 287 Log.d("Connected devices: " + sHspProfile.getConnectedDevices()); 288 BluetoothDevice device = null; 289 if (sHspProfile.getConnectedDevices().size() > 1) { 290 Log.d("More than one device available"); 291 } 292 try { 293 device = BluetoothFacade.getDevice( 294 sHspProfile.getConnectedDevices(), deviceAddress); 295 } catch (Exception e) { 296 Log.d("Cannot find device " + deviceAddress); 297 return false; 298 } 299 return sHspProfile.isAudioConnected(device); 300 } 301 302 /** 303 * Start voice recognition. Send BVRA command. 304 * 305 * @param deviceAddress the Bluetooth MAC address of remote device 306 * @return True if started successfully, False otherwise. 307 */ 308 @Rpc(description = "Start Voice Recognition.") 309 public Boolean bluetoothHspStartVoiceRecognition( 310 @RpcParameter(name = "deviceAddress", 311 description = "MAC address of a bluetooth device.") 312 String deviceAddress) throws Exception { 313 BluetoothDevice device = BluetoothFacade.getDevice( 314 sHspProfile.getConnectedDevices(), deviceAddress); 315 return sHspProfile.startVoiceRecognition(device); 316 } 317 318 /** 319 * Stop voice recognition. Send BVRA command. 320 * 321 * @param deviceAddress the Bluetooth MAC address of remote device 322 * @return True if stopped successfully, False otherwise. 323 */ 324 @Rpc(description = "Stop Voice Recognition.") 325 public Boolean bluetoothHspStopVoiceRecognition( 326 @RpcParameter(name = "deviceAddress", 327 description = "MAC address of a bluetooth device.") 328 String deviceAddress) throws Exception { 329 BluetoothDevice device = BluetoothFacade.getDevice( 330 sHspProfile.getConnectedDevices(), deviceAddress); 331 return sHspProfile.stopVoiceRecognition(device); 332 } 333 334 /** 335 * Determine whether in-band ringtone is enabled or not. 336 * 337 * @return True if enabled, False otherwise. 338 */ 339 @Rpc(description = "In-band ringtone enabled check.") 340 public Boolean bluetoothHspIsInbandRingingEnabled() { 341 return sHspProfile.isInbandRingingEnabled(); 342 } 343 344 /** 345 * Send a CLCC response from Sl4a (experimental). 346 * 347 * @param index the index of the call 348 * @param direction the direction of the call 349 * @param status the status of the call 350 * @param mode the mode 351 * @param mpty the mpty value 352 * @param number the phone number 353 * @param type the type 354 */ 355 @Rpc(description = "Send generic clcc response.") 356 public void bluetoothHspClccResponse( 357 @RpcParameter(name = "index", description = "") Integer index, 358 @RpcParameter(name = "direction", description = "") Integer direction, 359 @RpcParameter(name = "status", description = "") Integer status, 360 @RpcParameter(name = "mode", description = "") Integer mode, 361 @RpcParameter(name = "mpty", description = "") Boolean mpty, 362 @RpcParameter(name = "number", description = "") String number, 363 @RpcParameter(name = "type", description = "") Integer type 364 ) { 365 sHspProfile.clccResponse(index, direction, status, mode, mpty, number, type); 366 } 367 368 @Override 369 public void shutdown() { 370 } 371 } 372