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