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 * Is Hsp profile ready. 89 * @return if Hid profile is ready or not. 90 */ 91 @Rpc(description = "Is Hsp profile ready.") 92 public Boolean bluetoothHspIsReady() { 93 return sIsHspReady; 94 } 95 96 /** 97 * Set priority of the profile. 98 * @param deviceStr - name or MAC address of a Bluetooth device. 99 * @param priority - Priority that needs to be set. 100 */ 101 @Rpc(description = "Set priority of the profile.") 102 public void bluetoothHspSetPriority( 103 @RpcParameter(name = "device", description = "Mac address of a BT device.") 104 String deviceStr, 105 @RpcParameter(name = "priority", description = "Priority that needs to be set.") 106 Integer priority) throws Exception { 107 if (sHspProfile == null) return; 108 BluetoothDevice device = BluetoothFacade.getDevice( 109 mBluetoothAdapter.getBondedDevices(), deviceStr); 110 Log.d("Changing priority of device " + device.getAliasName() + " p: " + priority); 111 sHspProfile.setPriority(device, priority); 112 } 113 114 /** 115 * Connect to an HSP device. 116 * @param device - Name or MAC address of a bluetooth device. 117 * @return True if the connection was successful; otherwise False. 118 */ 119 @Rpc(description = "Connect to an HSP device.") 120 public Boolean bluetoothHspConnect( 121 @RpcParameter(name = "device", description = 122 "Name or MAC address of a bluetooth device.") 123 String device) throws Exception { 124 if (sHspProfile == null) return false; 125 BluetoothDevice mDevice = BluetoothFacade.getDevice( 126 mBluetoothAdapter.getBondedDevices(), device); 127 Log.d("Connecting to device " + mDevice.getAliasName()); 128 return hspConnect(mDevice); 129 } 130 131 /** 132 * Disconnect an HSP device. 133 * @param device - Name or MAC address of a bluetooth device. 134 * @return True if the disconnection was successful; otherwise False. 135 */ 136 @Rpc(description = "Disconnect an HSP device.") 137 public Boolean bluetoothHspDisconnect( 138 @RpcParameter(name = "device", description = "Name or MAC address of a device.") 139 String device) throws Exception { 140 if (sHspProfile == null) return false; 141 Log.d("Connected devices: " + sHspProfile.getConnectedDevices()); 142 BluetoothDevice mDevice = BluetoothFacade.getDevice( 143 sHspProfile.getConnectedDevices(), device); 144 return hspDisconnect(mDevice); 145 } 146 147 /** 148 * Get all the devices connected through HSP. 149 * @return List of all the devices connected through HSP. 150 */ 151 @Rpc(description = "Get all the devices connected through HSP.") 152 public List<BluetoothDevice> bluetoothHspGetConnectedDevices() { 153 if (!sIsHspReady) return null; 154 return sHspProfile.getConnectedDevices(); 155 } 156 157 /** 158 * Get the connection status of a device. 159 * @param deviceID - Name or MAC address of a bluetooth device. 160 * @return connection status of a device. 161 */ 162 @Rpc(description = "Get the connection status of a device.") 163 public Integer bluetoothHspGetConnectionStatus( 164 @RpcParameter(name = "deviceID", 165 description = "Name or MAC address of a bluetooth device.") 166 String deviceID) { 167 if (sHspProfile == null) { 168 return BluetoothProfile.STATE_DISCONNECTED; 169 } 170 List<BluetoothDevice> deviceList = sHspProfile.getConnectedDevices(); 171 BluetoothDevice device; 172 try { 173 device = BluetoothFacade.getDevice(deviceList, deviceID); 174 } catch (Exception e) { 175 return BluetoothProfile.STATE_DISCONNECTED; 176 } 177 return sHspProfile.getConnectionState(device); 178 } 179 180 /** 181 * Force SCO audio on DUT, ignore all other restrictions 182 * 183 * @param force True to force SCO audio, False to resume normal 184 * @return True if the setup is successful 185 */ 186 @Rpc(description = "Force SCO audio connection on DUT.") 187 public Boolean bluetoothHspForceScoAudio( 188 @RpcParameter(name = "force", description = "whether to force SCO audio") 189 Boolean force) { 190 if (sHspProfile == null) { 191 return false; 192 } 193 sHspProfile.setForceScoAudio(force); 194 return true; 195 } 196 197 /** 198 * Connect SCO audio to a remote device 199 * 200 * @param deviceAddress the Bluetooth MAC address of remote device 201 * @return True if connection is successful, False otherwise 202 */ 203 @Rpc(description = "Connect SCO audio for a remote device.") 204 public Boolean bluetoothHspConnectAudio( 205 @RpcParameter(name = "deviceAddress", 206 description = "MAC address of a bluetooth device.") 207 String deviceAddress) { 208 if (sHspProfile == null) { 209 return false; 210 } 211 Log.d("Connected devices: " + sHspProfile.getConnectedDevices()); 212 BluetoothDevice device = null; 213 if (sHspProfile.getConnectedDevices().size() > 1) { 214 Log.d("More than one device available"); 215 } 216 try { 217 device = BluetoothFacade.getDevice( 218 sHspProfile.getConnectedDevices(), deviceAddress); 219 } catch (Exception e) { 220 Log.d("Cannot find device " + deviceAddress); 221 return false; 222 } 223 return sHspProfile.connectAudio(); 224 } 225 226 /** 227 * Disconnect SCO audio for a remote device 228 * 229 * @param deviceAddress the Bluetooth MAC address of remote device 230 * @return True if disconnection is successful, False otherwise 231 */ 232 @Rpc(description = "Disconnect SCO audio for a remote device") 233 public Boolean bluetoothHspDisconnectAudio( 234 @RpcParameter(name = "deviceAddress", 235 description = "MAC address of a bluetooth device.") 236 String deviceAddress) { 237 if (sHspProfile == null) { 238 return false; 239 } 240 Log.d("Connected devices: " + sHspProfile.getConnectedDevices()); 241 BluetoothDevice device = null; 242 if (sHspProfile.getConnectedDevices().size() > 1) { 243 Log.d("More than one device available"); 244 } 245 try { 246 device = BluetoothFacade.getDevice( 247 sHspProfile.getConnectedDevices(), deviceAddress); 248 } catch (Exception e) { 249 Log.d("Cannot find device " + deviceAddress); 250 return false; 251 } 252 if (!sHspProfile.isAudioConnected(device)) { 253 Log.d("SCO audio is not connected for device " + deviceAddress); 254 return false; 255 } 256 return sHspProfile.disconnectAudio(); 257 } 258 259 /** 260 * Check if SCO audio is connected for a remote device 261 * 262 * @param deviceAddress the Bluetooth MAC address of remote device 263 * @return True if device is connected to us via SCO audio, False otherwise 264 */ 265 @Rpc(description = "Check if SCO audio is connected for a remote device") 266 public Boolean bluetoothHspIsAudioConnected( 267 @RpcParameter(name = "deviceAddress", 268 description = "MAC address of a bluetooth device.") 269 String deviceAddress) { 270 if (sHspProfile == null) { 271 return false; 272 } 273 Log.d("Connected devices: " + sHspProfile.getConnectedDevices()); 274 BluetoothDevice device = null; 275 if (sHspProfile.getConnectedDevices().size() > 1) { 276 Log.d("More than one device available"); 277 } 278 try { 279 device = BluetoothFacade.getDevice( 280 sHspProfile.getConnectedDevices(), deviceAddress); 281 } catch (Exception e) { 282 Log.d("Cannot find device " + deviceAddress); 283 return false; 284 } 285 return sHspProfile.isAudioConnected(device); 286 } 287 288 /** 289 * Start voice recognition. Send BVRA command. 290 * 291 * @param deviceAddress the Bluetooth MAC address of remote device 292 * @return True if started successfully, False otherwise. 293 */ 294 @Rpc(description = "Start Voice Recognition.") 295 public Boolean bluetoothHspStartVoiceRecognition( 296 @RpcParameter(name = "deviceAddress", 297 description = "MAC address of a bluetooth device.") 298 String deviceAddress) throws Exception { 299 BluetoothDevice device = BluetoothFacade.getDevice( 300 sHspProfile.getConnectedDevices(), deviceAddress); 301 return sHspProfile.startVoiceRecognition(device); 302 } 303 304 /** 305 * Stop voice recognition. Send BVRA command. 306 * 307 * @param deviceAddress the Bluetooth MAC address of remote device 308 * @return True if stopped successfully, False otherwise. 309 */ 310 @Rpc(description = "Stop Voice Recognition.") 311 public Boolean bluetoothHspStopVoiceRecognition( 312 @RpcParameter(name = "deviceAddress", 313 description = "MAC address of a bluetooth device.") 314 String deviceAddress) throws Exception { 315 BluetoothDevice device = BluetoothFacade.getDevice( 316 sHspProfile.getConnectedDevices(), deviceAddress); 317 return sHspProfile.stopVoiceRecognition(device); 318 } 319 320 /** 321 * Determine whether in-band ringtone is enabled or not. 322 * 323 * @return True if enabled, False otherwise. 324 */ 325 @Rpc(description = "In-band ringtone enabled check.") 326 public Boolean bluetoothHspIsInbandRingingEnabled() { 327 return sHspProfile.isInbandRingingEnabled(); 328 } 329 330 /** 331 * Send a CLCC response from Sl4a (experimental). 332 * 333 * @param index the index of the call 334 * @param direction the direction of the call 335 * @param status the status of the call 336 * @param mode the mode 337 * @param mpty the mpty value 338 * @param number the phone number 339 * @param type the type 340 */ 341 @Rpc(description = "Send generic clcc response.") 342 public void bluetoothHspClccResponse( 343 @RpcParameter(name = "index", description = "") Integer index, 344 @RpcParameter(name = "direction", description = "") Integer direction, 345 @RpcParameter(name = "status", description = "") Integer status, 346 @RpcParameter(name = "mode", description = "") Integer mode, 347 @RpcParameter(name = "mpty", description = "") Boolean mpty, 348 @RpcParameter(name = "number", description = "") String number, 349 @RpcParameter(name = "type", description = "") Integer type 350 ) { 351 sHspProfile.clccResponse(index, direction, status, mode, mpty, number, type); 352 } 353 354 @Override 355 public void shutdown() { 356 } 357 } 358