1 /* 2 * Copyright (C) 2012 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.bluetooth.hfp; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.bluetooth.BluetoothHeadset; 21 import android.bluetooth.BluetoothProfile; 22 import android.bluetooth.IBluetoothHeadset; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.media.AudioManager; 28 import android.os.Message; 29 import android.provider.Settings; 30 import android.util.Log; 31 import com.android.bluetooth.btservice.ProfileService; 32 import com.android.bluetooth.Utils; 33 import java.util.ArrayList; 34 import java.util.List; 35 36 /** 37 * Provides Bluetooth Headset and Handsfree profile, as a service in 38 * the Bluetooth application. 39 * @hide 40 */ 41 public class HeadsetService extends ProfileService { 42 private static final boolean DBG = false; 43 private static final String TAG = "HeadsetService"; 44 private static final String MODIFY_PHONE_STATE = android.Manifest.permission.MODIFY_PHONE_STATE; 45 46 private HeadsetStateMachine mStateMachine; 47 private static HeadsetService sHeadsetService; 48 getName()49 protected String getName() { 50 return TAG; 51 } 52 initBinder()53 public IProfileServiceBinder initBinder() { 54 return new BluetoothHeadsetBinder(this); 55 } 56 start()57 protected boolean start() { 58 mStateMachine = HeadsetStateMachine.make(this); 59 IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 60 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION); 61 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 62 try { 63 registerReceiver(mHeadsetReceiver, filter); 64 } catch (Exception e) { 65 Log.w(TAG, "Unable to register headset receiver", e); 66 } 67 setHeadsetService(this); 68 return true; 69 } 70 stop()71 protected boolean stop() { 72 try { 73 unregisterReceiver(mHeadsetReceiver); 74 } catch (Exception e) { 75 Log.w(TAG, "Unable to unregister headset receiver", e); 76 } 77 if (mStateMachine != null) { 78 mStateMachine.doQuit(); 79 } 80 return true; 81 } 82 cleanup()83 protected boolean cleanup() { 84 if (mStateMachine != null) { 85 mStateMachine.cleanup(); 86 } 87 clearHeadsetService(); 88 return true; 89 } 90 91 private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() { 92 @Override 93 public void onReceive(Context context, Intent intent) { 94 String action = intent.getAction(); 95 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 96 mStateMachine.sendMessage(HeadsetStateMachine.INTENT_BATTERY_CHANGED, intent); 97 } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) { 98 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 99 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) { 100 mStateMachine.sendMessage( 101 HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, intent); 102 } 103 } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 104 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 105 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 106 if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 107 Log.v(TAG, "Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY"); 108 mStateMachine.handleAccessPermissionResult(intent); 109 } 110 } 111 } 112 }; 113 114 /** 115 * Handlers for incoming service calls 116 */ 117 private static class BluetoothHeadsetBinder 118 extends IBluetoothHeadset.Stub implements IProfileServiceBinder { 119 private HeadsetService mService; 120 BluetoothHeadsetBinder(HeadsetService svc)121 public BluetoothHeadsetBinder(HeadsetService svc) { 122 mService = svc; 123 } cleanup()124 public boolean cleanup() { 125 mService = null; 126 return true; 127 } 128 getService()129 private HeadsetService getService() { 130 if (!Utils.checkCallerAllowManagedProfiles(mService)) { 131 Log.w(TAG, "Headset call not allowed for non-active user"); 132 return null; 133 } 134 135 if (mService != null && mService.isAvailable()) { 136 return mService; 137 } 138 return null; 139 } 140 connect(BluetoothDevice device)141 public boolean connect(BluetoothDevice device) { 142 HeadsetService service = getService(); 143 if (service == null) return false; 144 return service.connect(device); 145 } 146 disconnect(BluetoothDevice device)147 public boolean disconnect(BluetoothDevice device) { 148 HeadsetService service = getService(); 149 if (service == null) return false; 150 if (DBG) Log.d(TAG, "disconnect in HeadsetService"); 151 return service.disconnect(device); 152 } 153 getConnectedDevices()154 public List<BluetoothDevice> getConnectedDevices() { 155 HeadsetService service = getService(); 156 if (service == null) return new ArrayList<BluetoothDevice>(0); 157 return service.getConnectedDevices(); 158 } 159 getDevicesMatchingConnectionStates(int[] states)160 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 161 HeadsetService service = getService(); 162 if (service == null) return new ArrayList<BluetoothDevice>(0); 163 return service.getDevicesMatchingConnectionStates(states); 164 } 165 getConnectionState(BluetoothDevice device)166 public int getConnectionState(BluetoothDevice device) { 167 HeadsetService service = getService(); 168 if (service == null) return BluetoothProfile.STATE_DISCONNECTED; 169 return service.getConnectionState(device); 170 } 171 setPriority(BluetoothDevice device, int priority)172 public boolean setPriority(BluetoothDevice device, int priority) { 173 HeadsetService service = getService(); 174 if (service == null) return false; 175 return service.setPriority(device, priority); 176 } 177 getPriority(BluetoothDevice device)178 public int getPriority(BluetoothDevice device) { 179 HeadsetService service = getService(); 180 if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED; 181 return service.getPriority(device); 182 } 183 startVoiceRecognition(BluetoothDevice device)184 public boolean startVoiceRecognition(BluetoothDevice device) { 185 HeadsetService service = getService(); 186 if (service == null) return false; 187 return service.startVoiceRecognition(device); 188 } 189 stopVoiceRecognition(BluetoothDevice device)190 public boolean stopVoiceRecognition(BluetoothDevice device) { 191 HeadsetService service = getService(); 192 if (service == null) return false; 193 return service.stopVoiceRecognition(device); 194 } 195 isAudioOn()196 public boolean isAudioOn() { 197 HeadsetService service = getService(); 198 if (service == null) return false; 199 return service.isAudioOn(); 200 } 201 isAudioConnected(BluetoothDevice device)202 public boolean isAudioConnected(BluetoothDevice device) { 203 HeadsetService service = getService(); 204 if (service == null) return false; 205 return service.isAudioConnected(device); 206 } 207 getBatteryUsageHint(BluetoothDevice device)208 public int getBatteryUsageHint(BluetoothDevice device) { 209 HeadsetService service = getService(); 210 if (service == null) return 0; 211 return service.getBatteryUsageHint(device); 212 } 213 acceptIncomingConnect(BluetoothDevice device)214 public boolean acceptIncomingConnect(BluetoothDevice device) { 215 HeadsetService service = getService(); 216 if (service == null) return false; 217 return service.acceptIncomingConnect(device); 218 } 219 rejectIncomingConnect(BluetoothDevice device)220 public boolean rejectIncomingConnect(BluetoothDevice device) { 221 HeadsetService service = getService(); 222 if (service == null) return false; 223 return service.rejectIncomingConnect(device); 224 } 225 getAudioState(BluetoothDevice device)226 public int getAudioState(BluetoothDevice device) { 227 HeadsetService service = getService(); 228 if (service == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 229 return service.getAudioState(device); 230 } 231 connectAudio()232 public boolean connectAudio() { 233 HeadsetService service = getService(); 234 if (service == null) return false; 235 return service.connectAudio(); 236 } 237 disconnectAudio()238 public boolean disconnectAudio() { 239 HeadsetService service = getService(); 240 if (service == null) return false; 241 return service.disconnectAudio(); 242 } 243 setAudioRouteAllowed(boolean allowed)244 public void setAudioRouteAllowed(boolean allowed) { 245 HeadsetService service = getService(); 246 if (service == null) return; 247 service.setAudioRouteAllowed(allowed); 248 } 249 getAudioRouteAllowed()250 public boolean getAudioRouteAllowed() { 251 HeadsetService service = getService(); 252 if (service != null) { 253 return service.getAudioRouteAllowed(); 254 } 255 return false; 256 } 257 setForceScoAudio(boolean forced)258 public void setForceScoAudio(boolean forced) { 259 HeadsetService service = getService(); 260 if (service == null) return; 261 service.setForceScoAudio(forced); 262 } 263 startScoUsingVirtualVoiceCall(BluetoothDevice device)264 public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { 265 HeadsetService service = getService(); 266 if (service == null) return false; 267 return service.startScoUsingVirtualVoiceCall(device); 268 } 269 stopScoUsingVirtualVoiceCall(BluetoothDevice device)270 public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { 271 HeadsetService service = getService(); 272 if (service == null) return false; 273 return service.stopScoUsingVirtualVoiceCall(device); 274 } 275 phoneStateChanged( int numActive, int numHeld, int callState, String number, int type)276 public void phoneStateChanged( 277 int numActive, int numHeld, int callState, String number, int type) { 278 HeadsetService service = getService(); 279 if (service == null) return; 280 service.phoneStateChanged(numActive, numHeld, callState, number, type); 281 } 282 clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type)283 public void clccResponse(int index, int direction, int status, int mode, boolean mpty, 284 String number, int type) { 285 HeadsetService service = getService(); 286 if (service == null) return; 287 service.clccResponse(index, direction, status, mode, mpty, number, type); 288 } 289 sendVendorSpecificResultCode( BluetoothDevice device, String command, String arg)290 public boolean sendVendorSpecificResultCode( 291 BluetoothDevice device, String command, String arg) { 292 HeadsetService service = getService(); 293 if (service == null) { 294 return false; 295 } 296 return service.sendVendorSpecificResultCode(device, command, arg); 297 } 298 enableWBS()299 public boolean enableWBS() { 300 HeadsetService service = getService(); 301 if (service == null) return false; 302 return service.enableWBS(); 303 } 304 disableWBS()305 public boolean disableWBS() { 306 HeadsetService service = getService(); 307 if (service == null) return false; 308 return service.disableWBS(); 309 } 310 bindResponse(int ind_id, boolean ind_status)311 public void bindResponse(int ind_id, boolean ind_status) { 312 HeadsetService service = getService(); 313 if (service == null) return; 314 service.bindResponse(ind_id, ind_status); 315 } 316 }; 317 318 // API methods getHeadsetService()319 public static synchronized HeadsetService getHeadsetService() { 320 if (sHeadsetService != null && sHeadsetService.isAvailable()) { 321 if (DBG) Log.d(TAG, "getHeadsetService(): returning " + sHeadsetService); 322 return sHeadsetService; 323 } 324 if (DBG) { 325 if (sHeadsetService == null) { 326 Log.d(TAG, "getHeadsetService(): service is NULL"); 327 } else if (!(sHeadsetService.isAvailable())) { 328 Log.d(TAG, "getHeadsetService(): service is not available"); 329 } 330 } 331 return null; 332 } 333 setHeadsetService(HeadsetService instance)334 private static synchronized void setHeadsetService(HeadsetService instance) { 335 if (instance != null && instance.isAvailable()) { 336 if (DBG) Log.d(TAG, "setHeadsetService(): set to: " + sHeadsetService); 337 sHeadsetService = instance; 338 } else { 339 if (DBG) { 340 if (sHeadsetService == null) { 341 Log.d(TAG, "setHeadsetService(): service not available"); 342 } else if (!sHeadsetService.isAvailable()) { 343 Log.d(TAG, "setHeadsetService(): service is cleaning up"); 344 } 345 } 346 } 347 } 348 clearHeadsetService()349 private static synchronized void clearHeadsetService() { 350 sHeadsetService = null; 351 } 352 connect(BluetoothDevice device)353 public boolean connect(BluetoothDevice device) { 354 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 355 Log.d(TAG, "connect: device=" + device); 356 if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { 357 Log.w(TAG, "connect: PRIORITY_OFF, device=" + device); 358 return false; 359 } 360 int connectionState = mStateMachine.getConnectionState(device); 361 if (connectionState == BluetoothProfile.STATE_CONNECTED 362 || connectionState == BluetoothProfile.STATE_CONNECTING) { 363 Log.w(TAG, 364 "connect: already connected/connecting, connectionState=" + connectionState 365 + ", device=" + device); 366 return false; 367 } 368 mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device); 369 return true; 370 } 371 disconnect(BluetoothDevice device)372 boolean disconnect(BluetoothDevice device) { 373 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 374 Log.d(TAG, "disconnect: device=" + device); 375 int connectionState = mStateMachine.getConnectionState(device); 376 if (connectionState != BluetoothProfile.STATE_CONNECTED 377 && connectionState != BluetoothProfile.STATE_CONNECTING) { 378 Log.w(TAG, 379 "disconnect: not connected/connecting, connectionState=" + connectionState 380 + ", device=" + device); 381 return false; 382 } 383 mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device); 384 return true; 385 } 386 getConnectedDevices()387 public List<BluetoothDevice> getConnectedDevices() { 388 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 389 return mStateMachine.getConnectedDevices(); 390 } 391 getDevicesMatchingConnectionStates(int[] states)392 private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 393 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 394 return mStateMachine.getDevicesMatchingConnectionStates(states); 395 } 396 getConnectionState(BluetoothDevice device)397 public int getConnectionState(BluetoothDevice device) { 398 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 399 return mStateMachine.getConnectionState(device); 400 } 401 setPriority(BluetoothDevice device, int priority)402 public boolean setPriority(BluetoothDevice device, int priority) { 403 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 404 Settings.Global.putInt(getContentResolver(), 405 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), priority); 406 if (DBG) Log.d(TAG, "Saved priority " + device + " = " + priority); 407 return true; 408 } 409 getPriority(BluetoothDevice device)410 public int getPriority(BluetoothDevice device) { 411 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 412 int priority = Settings.Global.getInt(getContentResolver(), 413 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), 414 BluetoothProfile.PRIORITY_UNDEFINED); 415 return priority; 416 } 417 startVoiceRecognition(BluetoothDevice device)418 boolean startVoiceRecognition(BluetoothDevice device) { 419 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 420 int connectionState = mStateMachine.getConnectionState(device); 421 if (connectionState != BluetoothProfile.STATE_CONNECTED 422 && connectionState != BluetoothProfile.STATE_CONNECTING) { 423 return false; 424 } 425 mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START); 426 return true; 427 } 428 stopVoiceRecognition(BluetoothDevice device)429 boolean stopVoiceRecognition(BluetoothDevice device) { 430 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 431 // It seem that we really need to check the AudioOn state. 432 // But since we allow startVoiceRecognition in STATE_CONNECTED and 433 // STATE_CONNECTING state, we do these 2 in this method 434 int connectionState = mStateMachine.getConnectionState(device); 435 if (connectionState != BluetoothProfile.STATE_CONNECTED 436 && connectionState != BluetoothProfile.STATE_CONNECTING) { 437 return false; 438 } 439 mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP); 440 // TODO is this return correct when the voice recognition is not on? 441 return true; 442 } 443 isAudioOn()444 boolean isAudioOn() { 445 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 446 return mStateMachine.isAudioOn(); 447 } 448 isAudioConnected(BluetoothDevice device)449 boolean isAudioConnected(BluetoothDevice device) { 450 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 451 return mStateMachine.isAudioConnected(device); 452 } 453 getBatteryUsageHint(BluetoothDevice device)454 int getBatteryUsageHint(BluetoothDevice device) { 455 // TODO(BT) ask for BT stack support? 456 return 0; 457 } 458 acceptIncomingConnect(BluetoothDevice device)459 boolean acceptIncomingConnect(BluetoothDevice device) { 460 // TODO(BT) remove it if stack does access control 461 return false; 462 } 463 rejectIncomingConnect(BluetoothDevice device)464 boolean rejectIncomingConnect(BluetoothDevice device) { 465 // TODO(BT) remove it if stack does access control 466 return false; 467 } 468 getAudioState(BluetoothDevice device)469 int getAudioState(BluetoothDevice device) { 470 return mStateMachine.getAudioState(device); 471 } 472 setAudioRouteAllowed(boolean allowed)473 public void setAudioRouteAllowed(boolean allowed) { 474 mStateMachine.setAudioRouteAllowed(allowed); 475 } 476 getAudioRouteAllowed()477 public boolean getAudioRouteAllowed() { 478 return mStateMachine.getAudioRouteAllowed(); 479 } 480 setForceScoAudio(boolean forced)481 public void setForceScoAudio(boolean forced) { 482 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 483 mStateMachine.setForceScoAudio(forced); 484 } 485 connectAudio()486 boolean connectAudio() { 487 // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission 488 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 489 if (!mStateMachine.isConnected()) { 490 Log.w(TAG, "connectAudio: profile not connected"); 491 return false; 492 } 493 if (mStateMachine.isAudioOn()) { 494 Log.w(TAG, "connectAudio: audio is already ON"); 495 return false; 496 } 497 mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO); 498 return true; 499 } 500 disconnectAudio()501 boolean disconnectAudio() { 502 // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission 503 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 504 if (!mStateMachine.isAudioOn()) { 505 return false; 506 } 507 mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO); 508 return true; 509 } 510 startScoUsingVirtualVoiceCall(BluetoothDevice device)511 boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { 512 /* Do not ignore request if HSM state is still Disconnected or 513 Pending, it will be processed when transitioned to Connected */ 514 mStateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_START, device); 515 return true; 516 } 517 stopScoUsingVirtualVoiceCall(BluetoothDevice device)518 boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { 519 int connectionState = mStateMachine.getConnectionState(device); 520 if (connectionState != BluetoothProfile.STATE_CONNECTED 521 && connectionState != BluetoothProfile.STATE_CONNECTING) { 522 return false; 523 } 524 mStateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_STOP, device); 525 return true; 526 } 527 phoneStateChanged( int numActive, int numHeld, int callState, String number, int type)528 private void phoneStateChanged( 529 int numActive, int numHeld, int callState, String number, int type) { 530 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); 531 Message msg = mStateMachine.obtainMessage(HeadsetStateMachine.CALL_STATE_CHANGED); 532 msg.obj = new HeadsetCallState(numActive, numHeld, callState, number, type); 533 msg.arg1 = 0; // false 534 mStateMachine.sendMessage(msg); 535 } 536 clccResponse( int index, int direction, int status, int mode, boolean mpty, String number, int type)537 private void clccResponse( 538 int index, int direction, int status, int mode, boolean mpty, String number, int type) { 539 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); 540 mStateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE, 541 new HeadsetClccResponse(index, direction, status, mode, mpty, number, type)); 542 } 543 sendVendorSpecificResultCode( BluetoothDevice device, String command, String arg)544 private boolean sendVendorSpecificResultCode( 545 BluetoothDevice device, String command, String arg) { 546 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 547 int connectionState = mStateMachine.getConnectionState(device); 548 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 549 return false; 550 } 551 // Currently we support only "+ANDROID". 552 if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) { 553 Log.w(TAG, "Disallowed unsolicited result code command: " + command); 554 return false; 555 } 556 mStateMachine.sendMessage(HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE, 557 new HeadsetVendorSpecificResultCode(device, command, arg)); 558 return true; 559 } 560 enableWBS()561 boolean enableWBS() { 562 // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission 563 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 564 if (!mStateMachine.isConnected()) { 565 return false; 566 } 567 if (mStateMachine.isAudioOn()) { 568 return false; 569 } 570 571 for (BluetoothDevice device : getConnectedDevices()) { 572 mStateMachine.sendMessage(HeadsetStateMachine.ENABLE_WBS, device); 573 } 574 575 return true; 576 } 577 disableWBS()578 boolean disableWBS() { 579 // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission 580 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 581 if (!mStateMachine.isConnected()) { 582 return false; 583 } 584 if (mStateMachine.isAudioOn()) { 585 return false; 586 } 587 for (BluetoothDevice device : getConnectedDevices()) { 588 mStateMachine.sendMessage(HeadsetStateMachine.DISABLE_WBS, device); 589 } 590 return true; 591 } 592 bindResponse(int ind_id, boolean ind_status)593 private boolean bindResponse(int ind_id, boolean ind_status) { 594 for (BluetoothDevice device : getConnectedDevices()) { 595 int connectionState = mStateMachine.getConnectionState(device); 596 if (connectionState != BluetoothProfile.STATE_CONNECTED 597 && connectionState != BluetoothProfile.STATE_CONNECTING) { 598 continue; 599 } 600 if (DBG) Log.d("Bind Response sent for", device.getAddress()); 601 Message msg = mStateMachine.obtainMessage(HeadsetStateMachine.BIND_RESPONSE); 602 msg.obj = device; 603 msg.arg1 = ind_id; 604 msg.arg2 = ind_status ? 1 : 0; 605 mStateMachine.sendMessage(msg); 606 return true; 607 } 608 return false; 609 } 610 611 @Override dump(StringBuilder sb)612 public void dump(StringBuilder sb) { 613 super.dump(sb); 614 if (mStateMachine != null) { 615 mStateMachine.dump(sb); 616 } 617 } 618 } 619