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 static android.Manifest.permission.MODIFY_PHONE_STATE; 20 21 import android.annotation.Nullable; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothHeadset; 24 import android.bluetooth.BluetoothProfile; 25 import android.bluetooth.BluetoothUuid; 26 import android.bluetooth.IBluetoothHeadset; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.media.AudioManager; 32 import android.net.Uri; 33 import android.os.BatteryManager; 34 import android.os.HandlerThread; 35 import android.os.IDeviceIdleController; 36 import android.os.Looper; 37 import android.os.ParcelUuid; 38 import android.os.RemoteException; 39 import android.os.ServiceManager; 40 import android.os.SystemProperties; 41 import android.os.UserHandle; 42 import android.telecom.PhoneAccount; 43 import android.util.Log; 44 import android.util.StatsLog; 45 46 import com.android.bluetooth.BluetoothMetricsProto; 47 import com.android.bluetooth.Utils; 48 import com.android.bluetooth.btservice.AdapterService; 49 import com.android.bluetooth.btservice.MetricsLogger; 50 import com.android.bluetooth.btservice.ProfileService; 51 import com.android.internal.annotations.VisibleForTesting; 52 53 import java.util.ArrayList; 54 import java.util.Arrays; 55 import java.util.Comparator; 56 import java.util.HashMap; 57 import java.util.List; 58 import java.util.Objects; 59 60 /** 61 * Provides Bluetooth Headset and Handsfree profile, as a service in the Bluetooth application. 62 * 63 * Three modes for SCO audio: 64 * Mode 1: Telecom call through {@link #phoneStateChanged(int, int, int, String, int, String, 65 * boolean)} 66 * Mode 2: Virtual call through {@link #startScoUsingVirtualVoiceCall()} 67 * Mode 3: Voice recognition through {@link #startVoiceRecognition(BluetoothDevice)} 68 * 69 * When one mode is active, other mode cannot be started. API user has to terminate existing modes 70 * using the correct API or just {@link #disconnectAudio()} if user is a system service, before 71 * starting a new mode. 72 * 73 * {@link #connectAudio()} will start SCO audio at one of the above modes, but won't change mode 74 * {@link #disconnectAudio()} can happen in any mode to disconnect SCO 75 * 76 * When audio is disconnected, only Mode 1 Telecom call will be persisted, both Mode 2 virtual call 77 * and Mode 3 voice call will be terminated upon SCO termination and client has to restart the mode. 78 * 79 * NOTE: SCO termination can either be initiated on the AG side or the HF side 80 * TODO(b/79660380): As a workaround, voice recognition will be terminated if virtual call or 81 * Telecom call is initiated while voice recognition is ongoing, in case calling app did not call 82 * {@link #stopVoiceRecognition(BluetoothDevice)} 83 * 84 * AG - Audio Gateway, device running this {@link HeadsetService}, e.g. Android Phone 85 * HF - Handsfree device, device running headset client, e.g. Wireless headphones or car kits 86 */ 87 public class HeadsetService extends ProfileService { 88 private static final String TAG = "HeadsetService"; 89 private static final boolean DBG = false; 90 private static final String DISABLE_INBAND_RINGING_PROPERTY = 91 "persist.bluetooth.disableinbandringing"; 92 private static final ParcelUuid[] HEADSET_UUIDS = {BluetoothUuid.HSP, BluetoothUuid.Handsfree}; 93 private static final int[] CONNECTING_CONNECTED_STATES = 94 {BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED}; 95 private static final int DIALING_OUT_TIMEOUT_MS = 10000; 96 97 private int mMaxHeadsetConnections = 1; 98 private BluetoothDevice mActiveDevice; 99 private AdapterService mAdapterService; 100 private HandlerThread mStateMachinesThread; 101 // This is also used as a lock for shared data in HeadsetService 102 private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>(); 103 private HeadsetNativeInterface mNativeInterface; 104 private HeadsetSystemInterface mSystemInterface; 105 private boolean mAudioRouteAllowed = true; 106 // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions 107 private boolean mForceScoAudio; 108 private boolean mInbandRingingRuntimeDisable; 109 private boolean mVirtualCallStarted; 110 // Non null value indicates a pending dialing out event is going on 111 private DialingOutTimeoutEvent mDialingOutTimeoutEvent; 112 private boolean mVoiceRecognitionStarted; 113 // Non null value indicates a pending voice recognition request from headset is going on 114 private VoiceRecognitionTimeoutEvent mVoiceRecognitionTimeoutEvent; 115 // Timeout when voice recognition is started by remote device 116 @VisibleForTesting static int sStartVrTimeoutMs = 5000; 117 private boolean mStarted; 118 private boolean mCreated; 119 private static HeadsetService sHeadsetService; 120 121 @Override initBinder()122 public IProfileServiceBinder initBinder() { 123 return new BluetoothHeadsetBinder(this); 124 } 125 126 @Override create()127 protected void create() { 128 Log.i(TAG, "create()"); 129 if (mCreated) { 130 throw new IllegalStateException("create() called twice"); 131 } 132 mCreated = true; 133 } 134 135 @Override start()136 protected boolean start() { 137 Log.i(TAG, "start()"); 138 if (mStarted) { 139 throw new IllegalStateException("start() called twice"); 140 } 141 // Step 1: Get adapter service, should never be null 142 mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), 143 "AdapterService cannot be null when HeadsetService starts"); 144 // Step 2: Start handler thread for state machines 145 mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines"); 146 mStateMachinesThread.start(); 147 // Step 3: Initialize system interface 148 mSystemInterface = HeadsetObjectsFactory.getInstance().makeSystemInterface(this); 149 mSystemInterface.init(); 150 // Step 4: Initialize native interface 151 mMaxHeadsetConnections = mAdapterService.getMaxConnectedAudioDevices(); 152 mNativeInterface = HeadsetObjectsFactory.getInstance().getNativeInterface(); 153 // Add 1 to allow a pending device to be connecting or disconnecting 154 mNativeInterface.init(mMaxHeadsetConnections + 1, isInbandRingingEnabled()); 155 // Step 5: Check if state machine table is empty, crash if not 156 if (mStateMachines.size() > 0) { 157 throw new IllegalStateException( 158 "start(): mStateMachines is not empty, " + mStateMachines.size() 159 + " is already created. Was stop() called properly?"); 160 } 161 // Step 6: Setup broadcast receivers 162 IntentFilter filter = new IntentFilter(); 163 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 164 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION); 165 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 166 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 167 registerReceiver(mHeadsetReceiver, filter); 168 // Step 7: Mark service as started 169 setHeadsetService(this); 170 mStarted = true; 171 return true; 172 } 173 174 @Override stop()175 protected boolean stop() { 176 Log.i(TAG, "stop()"); 177 if (!mStarted) { 178 Log.w(TAG, "stop() called before start()"); 179 // Still return true because it is considered "stopped" and doesn't have any functional 180 // impact on the user 181 return true; 182 } 183 // Step 7: Mark service as stopped 184 mStarted = false; 185 setHeadsetService(null); 186 // Step 6: Tear down broadcast receivers 187 unregisterReceiver(mHeadsetReceiver); 188 synchronized (mStateMachines) { 189 // Reset active device to null 190 mActiveDevice = null; 191 mInbandRingingRuntimeDisable = false; 192 mForceScoAudio = false; 193 mAudioRouteAllowed = true; 194 mMaxHeadsetConnections = 1; 195 mVoiceRecognitionStarted = false; 196 mVirtualCallStarted = false; 197 if (mDialingOutTimeoutEvent != null) { 198 mStateMachinesThread.getThreadHandler().removeCallbacks(mDialingOutTimeoutEvent); 199 mDialingOutTimeoutEvent = null; 200 } 201 if (mVoiceRecognitionTimeoutEvent != null) { 202 mStateMachinesThread.getThreadHandler() 203 .removeCallbacks(mVoiceRecognitionTimeoutEvent); 204 mVoiceRecognitionTimeoutEvent = null; 205 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 206 mSystemInterface.getVoiceRecognitionWakeLock().release(); 207 } 208 } 209 // Step 5: Destroy state machines 210 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 211 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); 212 } 213 mStateMachines.clear(); 214 } 215 // Step 4: Destroy native interface 216 mNativeInterface.cleanup(); 217 // Step 3: Destroy system interface 218 mSystemInterface.stop(); 219 // Step 2: Stop handler thread 220 mStateMachinesThread.quitSafely(); 221 mStateMachinesThread = null; 222 // Step 1: Clear 223 synchronized (mStateMachines) { 224 mAdapterService = null; 225 } 226 return true; 227 } 228 229 @Override cleanup()230 protected void cleanup() { 231 Log.i(TAG, "cleanup"); 232 if (!mCreated) { 233 Log.w(TAG, "cleanup() called before create()"); 234 } 235 mCreated = false; 236 } 237 238 /** 239 * Checks if this service object is able to accept binder calls 240 * 241 * @return True if the object can accept binder calls, False otherwise 242 */ isAlive()243 public boolean isAlive() { 244 return isAvailable() && mCreated && mStarted; 245 } 246 247 /** 248 * Get the {@link Looper} for the state machine thread. This is used in testing and helper 249 * objects 250 * 251 * @return {@link Looper} for the state machine thread 252 */ 253 @VisibleForTesting getStateMachinesThreadLooper()254 public Looper getStateMachinesThreadLooper() { 255 return mStateMachinesThread.getLooper(); 256 } 257 258 interface StateMachineTask { execute(HeadsetStateMachine stateMachine)259 void execute(HeadsetStateMachine stateMachine); 260 } 261 doForStateMachine(BluetoothDevice device, StateMachineTask task)262 private boolean doForStateMachine(BluetoothDevice device, StateMachineTask task) { 263 synchronized (mStateMachines) { 264 HeadsetStateMachine stateMachine = mStateMachines.get(device); 265 if (stateMachine == null) { 266 return false; 267 } 268 task.execute(stateMachine); 269 } 270 return true; 271 } 272 doForEachConnectedStateMachine(StateMachineTask task)273 private void doForEachConnectedStateMachine(StateMachineTask task) { 274 synchronized (mStateMachines) { 275 for (BluetoothDevice device : getConnectedDevices()) { 276 task.execute(mStateMachines.get(device)); 277 } 278 } 279 } 280 onDeviceStateChanged(HeadsetDeviceState deviceState)281 void onDeviceStateChanged(HeadsetDeviceState deviceState) { 282 doForEachConnectedStateMachine( 283 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, 284 deviceState)); 285 } 286 287 /** 288 * Handle messages from native (JNI) to Java. This needs to be synchronized to avoid posting 289 * messages to state machine before start() is done 290 * 291 * @param stackEvent event from native stack 292 */ messageFromNative(HeadsetStackEvent stackEvent)293 void messageFromNative(HeadsetStackEvent stackEvent) { 294 Objects.requireNonNull(stackEvent.device, 295 "Device should never be null, event: " + stackEvent); 296 synchronized (mStateMachines) { 297 HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device); 298 if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { 299 switch (stackEvent.valueInt) { 300 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 301 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: { 302 // Create new state machine if none is found 303 if (stateMachine == null) { 304 stateMachine = HeadsetObjectsFactory.getInstance() 305 .makeStateMachine(stackEvent.device, 306 mStateMachinesThread.getLooper(), this, mAdapterService, 307 mNativeInterface, mSystemInterface); 308 mStateMachines.put(stackEvent.device, stateMachine); 309 } 310 break; 311 } 312 } 313 } 314 if (stateMachine == null) { 315 throw new IllegalStateException( 316 "State machine not found for stack event: " + stackEvent); 317 } 318 stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent); 319 } 320 } 321 322 private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() { 323 @Override 324 public void onReceive(Context context, Intent intent) { 325 String action = intent.getAction(); 326 if (action == null) { 327 Log.w(TAG, "mHeadsetReceiver, action is null"); 328 return; 329 } 330 switch (action) { 331 case Intent.ACTION_BATTERY_CHANGED: { 332 int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); 333 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); 334 if (batteryLevel < 0 || scale <= 0) { 335 Log.e(TAG, "Bad Battery Changed intent: batteryLevel=" + batteryLevel 336 + ", scale=" + scale); 337 return; 338 } 339 int cindBatteryLevel = Math.round(batteryLevel * 5 / ((float) scale)); 340 mSystemInterface.getHeadsetPhoneState().setCindBatteryCharge(cindBatteryLevel); 341 break; 342 } 343 case AudioManager.VOLUME_CHANGED_ACTION: { 344 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 345 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) { 346 doForEachConnectedStateMachine(stateMachine -> stateMachine.sendMessage( 347 HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, intent)); 348 } 349 break; 350 } 351 case BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY: { 352 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 353 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 354 BluetoothDevice device = 355 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 356 logD("Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, device=" + device 357 + ", type=" + requestType); 358 if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 359 synchronized (mStateMachines) { 360 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 361 if (stateMachine == null) { 362 Log.wtfStack(TAG, "Cannot find state machine for " + device); 363 return; 364 } 365 stateMachine.sendMessage( 366 HeadsetStateMachine.INTENT_CONNECTION_ACCESS_REPLY, intent); 367 } 368 } 369 break; 370 } 371 case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { 372 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 373 BluetoothDevice.ERROR); 374 BluetoothDevice device = Objects.requireNonNull( 375 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE), 376 "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 377 logD("Bond state changed for device: " + device + " state: " + state); 378 if (state != BluetoothDevice.BOND_NONE) { 379 break; 380 } 381 synchronized (mStateMachines) { 382 HeadsetStateMachine stateMachine = mStateMachines.get(device); 383 if (stateMachine == null) { 384 break; 385 } 386 if (stateMachine.getConnectionState() 387 != BluetoothProfile.STATE_DISCONNECTED) { 388 break; 389 } 390 removeStateMachine(device); 391 } 392 break; 393 } 394 default: 395 Log.w(TAG, "Unknown action " + action); 396 } 397 } 398 }; 399 400 /** 401 * Handlers for incoming service calls 402 */ 403 private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub 404 implements IProfileServiceBinder { 405 private volatile HeadsetService mService; 406 BluetoothHeadsetBinder(HeadsetService svc)407 BluetoothHeadsetBinder(HeadsetService svc) { 408 mService = svc; 409 } 410 411 @Override cleanup()412 public void cleanup() { 413 mService = null; 414 } 415 getService()416 private HeadsetService getService() { 417 final HeadsetService service = mService; 418 if (!Utils.checkCallerAllowManagedProfiles(service)) { 419 Log.w(TAG, "Headset call not allowed for non-active user"); 420 return null; 421 } 422 if (service == null) { 423 Log.w(TAG, "Service is null"); 424 return null; 425 } 426 if (!service.isAlive()) { 427 Log.w(TAG, "Service is not alive"); 428 return null; 429 } 430 return service; 431 } 432 433 @Override connect(BluetoothDevice device)434 public boolean connect(BluetoothDevice device) { 435 HeadsetService service = getService(); 436 if (service == null) { 437 return false; 438 } 439 return service.connect(device); 440 } 441 442 @Override disconnect(BluetoothDevice device)443 public boolean disconnect(BluetoothDevice device) { 444 HeadsetService service = getService(); 445 if (service == null) { 446 return false; 447 } 448 return service.disconnect(device); 449 } 450 451 @Override getConnectedDevices()452 public List<BluetoothDevice> getConnectedDevices() { 453 HeadsetService service = getService(); 454 if (service == null) { 455 return new ArrayList<BluetoothDevice>(0); 456 } 457 return service.getConnectedDevices(); 458 } 459 460 @Override getDevicesMatchingConnectionStates(int[] states)461 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 462 HeadsetService service = getService(); 463 if (service == null) { 464 return new ArrayList<BluetoothDevice>(0); 465 } 466 return service.getDevicesMatchingConnectionStates(states); 467 } 468 469 @Override getConnectionState(BluetoothDevice device)470 public int getConnectionState(BluetoothDevice device) { 471 HeadsetService service = getService(); 472 if (service == null) { 473 return BluetoothProfile.STATE_DISCONNECTED; 474 } 475 return service.getConnectionState(device); 476 } 477 478 @Override setPriority(BluetoothDevice device, int priority)479 public boolean setPriority(BluetoothDevice device, int priority) { 480 HeadsetService service = getService(); 481 if (service == null) { 482 return false; 483 } 484 return service.setPriority(device, priority); 485 } 486 487 @Override getPriority(BluetoothDevice device)488 public int getPriority(BluetoothDevice device) { 489 HeadsetService service = getService(); 490 if (service == null) { 491 return BluetoothProfile.PRIORITY_UNDEFINED; 492 } 493 return service.getPriority(device); 494 } 495 496 @Override startVoiceRecognition(BluetoothDevice device)497 public boolean startVoiceRecognition(BluetoothDevice device) { 498 HeadsetService service = getService(); 499 if (service == null) { 500 return false; 501 } 502 return service.startVoiceRecognition(device); 503 } 504 505 @Override stopVoiceRecognition(BluetoothDevice device)506 public boolean stopVoiceRecognition(BluetoothDevice device) { 507 HeadsetService service = getService(); 508 if (service == null) { 509 return false; 510 } 511 return service.stopVoiceRecognition(device); 512 } 513 514 @Override isAudioOn()515 public boolean isAudioOn() { 516 HeadsetService service = getService(); 517 if (service == null) { 518 return false; 519 } 520 return service.isAudioOn(); 521 } 522 523 @Override isAudioConnected(BluetoothDevice device)524 public boolean isAudioConnected(BluetoothDevice device) { 525 HeadsetService service = getService(); 526 if (service == null) { 527 return false; 528 } 529 return service.isAudioConnected(device); 530 } 531 532 @Override getAudioState(BluetoothDevice device)533 public int getAudioState(BluetoothDevice device) { 534 HeadsetService service = getService(); 535 if (service == null) { 536 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 537 } 538 return service.getAudioState(device); 539 } 540 541 @Override connectAudio()542 public boolean connectAudio() { 543 HeadsetService service = getService(); 544 if (service == null) { 545 return false; 546 } 547 return service.connectAudio(); 548 } 549 550 @Override disconnectAudio()551 public boolean disconnectAudio() { 552 HeadsetService service = getService(); 553 if (service == null) { 554 return false; 555 } 556 return service.disconnectAudio(); 557 } 558 559 @Override setAudioRouteAllowed(boolean allowed)560 public void setAudioRouteAllowed(boolean allowed) { 561 HeadsetService service = getService(); 562 if (service == null) { 563 return; 564 } 565 service.setAudioRouteAllowed(allowed); 566 } 567 568 @Override getAudioRouteAllowed()569 public boolean getAudioRouteAllowed() { 570 HeadsetService service = getService(); 571 if (service != null) { 572 return service.getAudioRouteAllowed(); 573 } 574 return false; 575 } 576 577 @Override setForceScoAudio(boolean forced)578 public void setForceScoAudio(boolean forced) { 579 HeadsetService service = getService(); 580 if (service == null) { 581 return; 582 } 583 service.setForceScoAudio(forced); 584 } 585 586 @Override startScoUsingVirtualVoiceCall()587 public boolean startScoUsingVirtualVoiceCall() { 588 HeadsetService service = getService(); 589 if (service == null) { 590 return false; 591 } 592 return service.startScoUsingVirtualVoiceCall(); 593 } 594 595 @Override stopScoUsingVirtualVoiceCall()596 public boolean stopScoUsingVirtualVoiceCall() { 597 HeadsetService service = getService(); 598 if (service == null) { 599 return false; 600 } 601 return service.stopScoUsingVirtualVoiceCall(); 602 } 603 604 @Override phoneStateChanged(int numActive, int numHeld, int callState, String number, int type, String name)605 public void phoneStateChanged(int numActive, int numHeld, int callState, String number, 606 int type, String name) { 607 HeadsetService service = getService(); 608 if (service == null) { 609 return; 610 } 611 service.phoneStateChanged(numActive, numHeld, callState, number, type, name, false); 612 } 613 614 @Override clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type)615 public void clccResponse(int index, int direction, int status, int mode, boolean mpty, 616 String number, int type) { 617 HeadsetService service = getService(); 618 if (service == null) { 619 return; 620 } 621 service.clccResponse(index, direction, status, mode, mpty, number, type); 622 } 623 624 @Override sendVendorSpecificResultCode(BluetoothDevice device, String command, String arg)625 public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, 626 String arg) { 627 HeadsetService service = getService(); 628 if (service == null) { 629 return false; 630 } 631 return service.sendVendorSpecificResultCode(device, command, arg); 632 } 633 634 @Override setActiveDevice(BluetoothDevice device)635 public boolean setActiveDevice(BluetoothDevice device) { 636 HeadsetService service = getService(); 637 if (service == null) { 638 return false; 639 } 640 return service.setActiveDevice(device); 641 } 642 643 @Override getActiveDevice()644 public BluetoothDevice getActiveDevice() { 645 HeadsetService service = getService(); 646 if (service == null) { 647 return null; 648 } 649 return service.getActiveDevice(); 650 } 651 652 @Override isInbandRingingEnabled()653 public boolean isInbandRingingEnabled() { 654 HeadsetService service = getService(); 655 if (service == null) { 656 return false; 657 } 658 return service.isInbandRingingEnabled(); 659 } 660 } 661 662 // API methods getHeadsetService()663 public static synchronized HeadsetService getHeadsetService() { 664 if (sHeadsetService == null) { 665 Log.w(TAG, "getHeadsetService(): service is NULL"); 666 return null; 667 } 668 if (!sHeadsetService.isAvailable()) { 669 Log.w(TAG, "getHeadsetService(): service is not available"); 670 return null; 671 } 672 logD("getHeadsetService(): returning " + sHeadsetService); 673 return sHeadsetService; 674 } 675 setHeadsetService(HeadsetService instance)676 private static synchronized void setHeadsetService(HeadsetService instance) { 677 logD("setHeadsetService(): set to: " + instance); 678 sHeadsetService = instance; 679 } 680 connect(BluetoothDevice device)681 public boolean connect(BluetoothDevice device) { 682 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 683 if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { 684 Log.w(TAG, "connect: PRIORITY_OFF, device=" + device + ", " + Utils.getUidPidString()); 685 return false; 686 } 687 ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 688 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 689 Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID, " 690 + Utils.getUidPidString()); 691 return false; 692 } 693 synchronized (mStateMachines) { 694 Log.i(TAG, "connect: device=" + device + ", " + Utils.getUidPidString()); 695 HeadsetStateMachine stateMachine = mStateMachines.get(device); 696 if (stateMachine == null) { 697 stateMachine = HeadsetObjectsFactory.getInstance() 698 .makeStateMachine(device, mStateMachinesThread.getLooper(), this, 699 mAdapterService, mNativeInterface, mSystemInterface); 700 mStateMachines.put(device, stateMachine); 701 } 702 int connectionState = stateMachine.getConnectionState(); 703 if (connectionState == BluetoothProfile.STATE_CONNECTED 704 || connectionState == BluetoothProfile.STATE_CONNECTING) { 705 Log.w(TAG, "connect: device " + device 706 + " is already connected/connecting, connectionState=" + connectionState); 707 return false; 708 } 709 List<BluetoothDevice> connectingConnectedDevices = 710 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 711 boolean disconnectExisting = false; 712 if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) { 713 // When there is maximum one device, we automatically disconnect the current one 714 if (mMaxHeadsetConnections == 1) { 715 disconnectExisting = true; 716 } else { 717 Log.w(TAG, "Max connection has reached, rejecting connection to " + device); 718 return false; 719 } 720 } 721 if (disconnectExisting) { 722 for (BluetoothDevice connectingConnectedDevice : connectingConnectedDevices) { 723 disconnect(connectingConnectedDevice); 724 } 725 setActiveDevice(null); 726 } 727 stateMachine.sendMessage(HeadsetStateMachine.CONNECT, device); 728 } 729 return true; 730 } 731 disconnect(BluetoothDevice device)732 boolean disconnect(BluetoothDevice device) { 733 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 734 Log.i(TAG, "disconnect: device=" + device + ", " + Utils.getUidPidString()); 735 synchronized (mStateMachines) { 736 HeadsetStateMachine stateMachine = mStateMachines.get(device); 737 if (stateMachine == null) { 738 Log.w(TAG, "disconnect: device " + device + " not ever connected/connecting"); 739 return false; 740 } 741 int connectionState = stateMachine.getConnectionState(); 742 if (connectionState != BluetoothProfile.STATE_CONNECTED 743 && connectionState != BluetoothProfile.STATE_CONNECTING) { 744 Log.w(TAG, "disconnect: device " + device 745 + " not connected/connecting, connectionState=" + connectionState); 746 return false; 747 } 748 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device); 749 } 750 return true; 751 } 752 getConnectedDevices()753 public List<BluetoothDevice> getConnectedDevices() { 754 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 755 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 756 synchronized (mStateMachines) { 757 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 758 if (stateMachine.getConnectionState() == BluetoothProfile.STATE_CONNECTED) { 759 devices.add(stateMachine.getDevice()); 760 } 761 } 762 } 763 return devices; 764 } 765 766 /** 767 * Same as the API method {@link BluetoothHeadset#getDevicesMatchingConnectionStates(int[])} 768 * 769 * @param states an array of states from {@link BluetoothProfile} 770 * @return a list of devices matching the array of connection states 771 */ 772 @VisibleForTesting getDevicesMatchingConnectionStates(int[] states)773 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 774 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 775 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 776 synchronized (mStateMachines) { 777 if (states == null || mAdapterService == null) { 778 return devices; 779 } 780 final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 781 if (bondedDevices == null) { 782 return devices; 783 } 784 for (BluetoothDevice device : bondedDevices) { 785 final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 786 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 787 continue; 788 } 789 int connectionState = getConnectionState(device); 790 for (int state : states) { 791 if (connectionState == state) { 792 devices.add(device); 793 break; 794 } 795 } 796 } 797 } 798 return devices; 799 } 800 getConnectionState(BluetoothDevice device)801 public int getConnectionState(BluetoothDevice device) { 802 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 803 synchronized (mStateMachines) { 804 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 805 if (stateMachine == null) { 806 return BluetoothProfile.STATE_DISCONNECTED; 807 } 808 return stateMachine.getConnectionState(); 809 } 810 } 811 setPriority(BluetoothDevice device, int priority)812 public boolean setPriority(BluetoothDevice device, int priority) { 813 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 814 Log.i(TAG, "setPriority: device=" + device + ", priority=" + priority + ", " 815 + Utils.getUidPidString()); 816 mAdapterService.getDatabase() 817 .setProfilePriority(device, BluetoothProfile.HEADSET, priority); 818 return true; 819 } 820 getPriority(BluetoothDevice device)821 public int getPriority(BluetoothDevice device) { 822 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 823 return mAdapterService.getDatabase() 824 .getProfilePriority(device, BluetoothProfile.HEADSET); 825 } 826 startVoiceRecognition(BluetoothDevice device)827 boolean startVoiceRecognition(BluetoothDevice device) { 828 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 829 Log.i(TAG, "startVoiceRecognition: device=" + device + ", " + Utils.getUidPidString()); 830 synchronized (mStateMachines) { 831 // TODO(b/79660380): Workaround in case voice recognition was not terminated properly 832 if (mVoiceRecognitionStarted) { 833 boolean status = stopVoiceRecognition(mActiveDevice); 834 Log.w(TAG, "startVoiceRecognition: voice recognition is still active, just called " 835 + "stopVoiceRecognition, returned " + status + " on " + mActiveDevice 836 + ", please try again"); 837 mVoiceRecognitionStarted = false; 838 return false; 839 } 840 if (!isAudioModeIdle()) { 841 Log.w(TAG, "startVoiceRecognition: audio mode not idle, active device is " 842 + mActiveDevice); 843 return false; 844 } 845 // Audio should not be on when no audio mode is active 846 if (isAudioOn()) { 847 // Disconnect audio so that API user can try later 848 boolean status = disconnectAudio(); 849 Log.w(TAG, "startVoiceRecognition: audio is still active, please wait for audio to" 850 + " be disconnected, disconnectAudio() returned " + status 851 + ", active device is " + mActiveDevice); 852 return false; 853 } 854 if (device == null) { 855 Log.i(TAG, "device is null, use active device " + mActiveDevice + " instead"); 856 device = mActiveDevice; 857 } 858 boolean pendingRequestByHeadset = false; 859 if (mVoiceRecognitionTimeoutEvent != null) { 860 if (!mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice.equals(device)) { 861 // TODO(b/79660380): Workaround when target device != requesting device 862 Log.w(TAG, "startVoiceRecognition: device " + device 863 + " is not the same as requesting device " 864 + mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice 865 + ", fall back to requesting device"); 866 device = mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice; 867 } 868 mStateMachinesThread.getThreadHandler() 869 .removeCallbacks(mVoiceRecognitionTimeoutEvent); 870 mVoiceRecognitionTimeoutEvent = null; 871 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 872 mSystemInterface.getVoiceRecognitionWakeLock().release(); 873 } 874 pendingRequestByHeadset = true; 875 } 876 if (!Objects.equals(device, mActiveDevice) && !setActiveDevice(device)) { 877 Log.w(TAG, "startVoiceRecognition: failed to set " + device + " as active"); 878 return false; 879 } 880 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 881 if (stateMachine == null) { 882 Log.w(TAG, "startVoiceRecognition: " + device + " is never connected"); 883 return false; 884 } 885 int connectionState = stateMachine.getConnectionState(); 886 if (connectionState != BluetoothProfile.STATE_CONNECTED 887 && connectionState != BluetoothProfile.STATE_CONNECTING) { 888 Log.w(TAG, "startVoiceRecognition: " + device + " is not connected or connecting"); 889 return false; 890 } 891 mVoiceRecognitionStarted = true; 892 if (pendingRequestByHeadset) { 893 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_RESULT, 894 1 /* success */, 0, device); 895 } else { 896 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START, device); 897 } 898 stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); 899 } 900 return true; 901 } 902 stopVoiceRecognition(BluetoothDevice device)903 boolean stopVoiceRecognition(BluetoothDevice device) { 904 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 905 Log.i(TAG, "stopVoiceRecognition: device=" + device + ", " + Utils.getUidPidString()); 906 synchronized (mStateMachines) { 907 if (!Objects.equals(mActiveDevice, device)) { 908 Log.w(TAG, "startVoiceRecognition: requested device " + device 909 + " is not active, use active device " + mActiveDevice + " instead"); 910 device = mActiveDevice; 911 } 912 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 913 if (stateMachine == null) { 914 Log.w(TAG, "stopVoiceRecognition: " + device + " is never connected"); 915 return false; 916 } 917 int connectionState = stateMachine.getConnectionState(); 918 if (connectionState != BluetoothProfile.STATE_CONNECTED 919 && connectionState != BluetoothProfile.STATE_CONNECTING) { 920 Log.w(TAG, "stopVoiceRecognition: " + device + " is not connected or connecting"); 921 return false; 922 } 923 if (!mVoiceRecognitionStarted) { 924 Log.w(TAG, "stopVoiceRecognition: voice recognition was not started"); 925 return false; 926 } 927 mVoiceRecognitionStarted = false; 928 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device); 929 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); 930 } 931 return true; 932 } 933 isAudioOn()934 boolean isAudioOn() { 935 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 936 return getNonIdleAudioDevices().size() > 0; 937 } 938 isAudioConnected(BluetoothDevice device)939 boolean isAudioConnected(BluetoothDevice device) { 940 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 941 synchronized (mStateMachines) { 942 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 943 if (stateMachine == null) { 944 return false; 945 } 946 return stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED; 947 } 948 } 949 getAudioState(BluetoothDevice device)950 int getAudioState(BluetoothDevice device) { 951 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 952 synchronized (mStateMachines) { 953 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 954 if (stateMachine == null) { 955 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 956 } 957 return stateMachine.getAudioState(); 958 } 959 } 960 setAudioRouteAllowed(boolean allowed)961 public void setAudioRouteAllowed(boolean allowed) { 962 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 963 Log.i(TAG, "setAudioRouteAllowed: allowed=" + allowed + ", " + Utils.getUidPidString()); 964 mAudioRouteAllowed = allowed; 965 mNativeInterface.setScoAllowed(allowed); 966 } 967 getAudioRouteAllowed()968 public boolean getAudioRouteAllowed() { 969 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 970 return mAudioRouteAllowed; 971 } 972 setForceScoAudio(boolean forced)973 public void setForceScoAudio(boolean forced) { 974 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 975 Log.i(TAG, "setForceScoAudio: forced=" + forced + ", " + Utils.getUidPidString()); 976 mForceScoAudio = forced; 977 } 978 979 @VisibleForTesting getForceScoAudio()980 public boolean getForceScoAudio() { 981 return mForceScoAudio; 982 } 983 984 /** 985 * Get first available device for SCO audio 986 * 987 * @return first connected headset device 988 */ 989 @VisibleForTesting 990 @Nullable getFirstConnectedAudioDevice()991 public BluetoothDevice getFirstConnectedAudioDevice() { 992 ArrayList<HeadsetStateMachine> stateMachines = new ArrayList<>(); 993 synchronized (mStateMachines) { 994 List<BluetoothDevice> availableDevices = 995 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 996 for (BluetoothDevice device : availableDevices) { 997 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 998 if (stateMachine == null) { 999 continue; 1000 } 1001 stateMachines.add(stateMachine); 1002 } 1003 } 1004 stateMachines.sort(Comparator.comparingLong(HeadsetStateMachine::getConnectingTimestampMs)); 1005 if (stateMachines.size() > 0) { 1006 return stateMachines.get(0).getDevice(); 1007 } 1008 return null; 1009 } 1010 1011 /** 1012 * Process a change in the silence mode for a {@link BluetoothDevice}. 1013 * 1014 * @param device the device to change silence mode 1015 * @param silence true to enable silence mode, false to disable. 1016 * @return true on success, false on error 1017 */ 1018 @VisibleForTesting setSilenceMode(BluetoothDevice device, boolean silence)1019 public boolean setSilenceMode(BluetoothDevice device, boolean silence) { 1020 Log.d(TAG, "setSilenceMode(" + device + "): " + silence); 1021 1022 if (silence && Objects.equals(mActiveDevice, device)) { 1023 setActiveDevice(null); 1024 } else if (!silence && mActiveDevice == null) { 1025 // Set the device as the active device if currently no active device. 1026 setActiveDevice(device); 1027 } 1028 synchronized (mStateMachines) { 1029 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1030 if (stateMachine == null) { 1031 Log.w(TAG, "setSilenceMode: device " + device 1032 + " was never connected/connecting"); 1033 return false; 1034 } 1035 stateMachine.setSilenceDevice(silence); 1036 } 1037 1038 return true; 1039 } 1040 1041 /** 1042 * Set the active device. 1043 * 1044 * @param device the active device 1045 * @return true on success, otherwise false 1046 */ setActiveDevice(BluetoothDevice device)1047 public boolean setActiveDevice(BluetoothDevice device) { 1048 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1049 Log.i(TAG, "setActiveDevice: device=" + device + ", " + Utils.getUidPidString()); 1050 synchronized (mStateMachines) { 1051 if (device == null) { 1052 // Clear the active device 1053 if (mVoiceRecognitionStarted) { 1054 if (!stopVoiceRecognition(mActiveDevice)) { 1055 Log.w(TAG, "setActiveDevice: fail to stopVoiceRecognition from " 1056 + mActiveDevice); 1057 } 1058 } 1059 if (mVirtualCallStarted) { 1060 if (!stopScoUsingVirtualVoiceCall()) { 1061 Log.w(TAG, "setActiveDevice: fail to stopScoUsingVirtualVoiceCall from " 1062 + mActiveDevice); 1063 } 1064 } 1065 if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1066 if (!disconnectAudio(mActiveDevice)) { 1067 Log.w(TAG, "setActiveDevice: disconnectAudio failed on " + mActiveDevice); 1068 } 1069 } 1070 mActiveDevice = null; 1071 broadcastActiveDevice(null); 1072 return true; 1073 } 1074 if (device.equals(mActiveDevice)) { 1075 Log.i(TAG, "setActiveDevice: device " + device + " is already active"); 1076 return true; 1077 } 1078 if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { 1079 Log.e(TAG, "setActiveDevice: Cannot set " + device 1080 + " as active, device is not connected"); 1081 return false; 1082 } 1083 if (!mNativeInterface.setActiveDevice(device)) { 1084 Log.e(TAG, "setActiveDevice: Cannot set " + device + " as active in native layer"); 1085 return false; 1086 } 1087 BluetoothDevice previousActiveDevice = mActiveDevice; 1088 mActiveDevice = device; 1089 if (getAudioState(previousActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1090 if (!disconnectAudio(previousActiveDevice)) { 1091 Log.e(TAG, "setActiveDevice: fail to disconnectAudio from " 1092 + previousActiveDevice); 1093 mActiveDevice = previousActiveDevice; 1094 mNativeInterface.setActiveDevice(previousActiveDevice); 1095 return false; 1096 } 1097 broadcastActiveDevice(mActiveDevice); 1098 } else if (shouldPersistAudio()) { 1099 broadcastActiveDevice(mActiveDevice); 1100 if (!connectAudio(mActiveDevice)) { 1101 Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice); 1102 mActiveDevice = previousActiveDevice; 1103 mNativeInterface.setActiveDevice(previousActiveDevice); 1104 return false; 1105 } 1106 } else { 1107 broadcastActiveDevice(mActiveDevice); 1108 } 1109 } 1110 return true; 1111 } 1112 1113 /** 1114 * Get the active device. 1115 * 1116 * @return the active device or null if no device is active 1117 */ getActiveDevice()1118 public BluetoothDevice getActiveDevice() { 1119 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1120 synchronized (mStateMachines) { 1121 return mActiveDevice; 1122 } 1123 } 1124 connectAudio()1125 boolean connectAudio() { 1126 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1127 synchronized (mStateMachines) { 1128 BluetoothDevice device = mActiveDevice; 1129 if (device == null) { 1130 Log.w(TAG, "connectAudio: no active device, " + Utils.getUidPidString()); 1131 return false; 1132 } 1133 return connectAudio(device); 1134 } 1135 } 1136 connectAudio(BluetoothDevice device)1137 boolean connectAudio(BluetoothDevice device) { 1138 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1139 Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString()); 1140 synchronized (mStateMachines) { 1141 if (!isScoAcceptable(device)) { 1142 Log.w(TAG, "connectAudio, rejected SCO request to " + device); 1143 return false; 1144 } 1145 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1146 if (stateMachine == null) { 1147 Log.w(TAG, "connectAudio: device " + device + " was never connected/connecting"); 1148 return false; 1149 } 1150 if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { 1151 Log.w(TAG, "connectAudio: profile not connected"); 1152 return false; 1153 } 1154 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1155 logD("connectAudio: audio is not idle for device " + device); 1156 return true; 1157 } 1158 if (isAudioOn()) { 1159 Log.w(TAG, "connectAudio: audio is not idle, current audio devices are " 1160 + Arrays.toString(getNonIdleAudioDevices().toArray())); 1161 return false; 1162 } 1163 stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); 1164 } 1165 return true; 1166 } 1167 getNonIdleAudioDevices()1168 private List<BluetoothDevice> getNonIdleAudioDevices() { 1169 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 1170 synchronized (mStateMachines) { 1171 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 1172 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1173 devices.add(stateMachine.getDevice()); 1174 } 1175 } 1176 } 1177 return devices; 1178 } 1179 disconnectAudio()1180 boolean disconnectAudio() { 1181 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1182 boolean result = false; 1183 synchronized (mStateMachines) { 1184 for (BluetoothDevice device : getNonIdleAudioDevices()) { 1185 if (disconnectAudio(device)) { 1186 result = true; 1187 } else { 1188 Log.e(TAG, "disconnectAudio() from " + device + " failed"); 1189 } 1190 } 1191 } 1192 if (!result) { 1193 logD("disconnectAudio() no active audio connection"); 1194 } 1195 return result; 1196 } 1197 disconnectAudio(BluetoothDevice device)1198 boolean disconnectAudio(BluetoothDevice device) { 1199 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1200 synchronized (mStateMachines) { 1201 Log.i(TAG, "disconnectAudio: device=" + device + ", " + Utils.getUidPidString()); 1202 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1203 if (stateMachine == null) { 1204 Log.w(TAG, "disconnectAudio: device " + device + " was never connected/connecting"); 1205 return false; 1206 } 1207 if (stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1208 Log.w(TAG, "disconnectAudio, audio is already disconnected for " + device); 1209 return false; 1210 } 1211 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); 1212 } 1213 return true; 1214 } 1215 isVirtualCallStarted()1216 boolean isVirtualCallStarted() { 1217 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1218 synchronized (mStateMachines) { 1219 return mVirtualCallStarted; 1220 } 1221 } 1222 startScoUsingVirtualVoiceCall()1223 private boolean startScoUsingVirtualVoiceCall() { 1224 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1225 Log.i(TAG, "startScoUsingVirtualVoiceCall: " + Utils.getUidPidString()); 1226 synchronized (mStateMachines) { 1227 // TODO(b/79660380): Workaround in case voice recognition was not terminated properly 1228 if (mVoiceRecognitionStarted) { 1229 boolean status = stopVoiceRecognition(mActiveDevice); 1230 Log.w(TAG, "startScoUsingVirtualVoiceCall: voice recognition is still active, " 1231 + "just called stopVoiceRecognition, returned " + status + " on " 1232 + mActiveDevice + ", please try again"); 1233 mVoiceRecognitionStarted = false; 1234 return false; 1235 } 1236 if (!isAudioModeIdle()) { 1237 Log.w(TAG, "startScoUsingVirtualVoiceCall: audio mode not idle, active device is " 1238 + mActiveDevice); 1239 return false; 1240 } 1241 // Audio should not be on when no audio mode is active 1242 if (isAudioOn()) { 1243 // Disconnect audio so that API user can try later 1244 boolean status = disconnectAudio(); 1245 Log.w(TAG, "startScoUsingVirtualVoiceCall: audio is still active, please wait for " 1246 + "audio to be disconnected, disconnectAudio() returned " + status 1247 + ", active device is " + mActiveDevice); 1248 return false; 1249 } 1250 if (mActiveDevice == null) { 1251 Log.w(TAG, "startScoUsingVirtualVoiceCall: no active device"); 1252 return false; 1253 } 1254 mVirtualCallStarted = true; 1255 // Send virtual phone state changed to initialize SCO 1256 phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0, "", true); 1257 phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0, "", true); 1258 phoneStateChanged(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, "", true); 1259 return true; 1260 } 1261 } 1262 stopScoUsingVirtualVoiceCall()1263 boolean stopScoUsingVirtualVoiceCall() { 1264 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1265 Log.i(TAG, "stopScoUsingVirtualVoiceCall: " + Utils.getUidPidString()); 1266 synchronized (mStateMachines) { 1267 // 1. Check if virtual call has already started 1268 if (!mVirtualCallStarted) { 1269 Log.w(TAG, "stopScoUsingVirtualVoiceCall: virtual call not started"); 1270 return false; 1271 } 1272 mVirtualCallStarted = false; 1273 // 2. Send virtual phone state changed to close SCO 1274 phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, "", true); 1275 } 1276 return true; 1277 } 1278 1279 class DialingOutTimeoutEvent implements Runnable { 1280 BluetoothDevice mDialingOutDevice; 1281 DialingOutTimeoutEvent(BluetoothDevice fromDevice)1282 DialingOutTimeoutEvent(BluetoothDevice fromDevice) { 1283 mDialingOutDevice = fromDevice; 1284 } 1285 1286 @Override run()1287 public void run() { 1288 synchronized (mStateMachines) { 1289 mDialingOutTimeoutEvent = null; 1290 doForStateMachine(mDialingOutDevice, stateMachine -> stateMachine.sendMessage( 1291 HeadsetStateMachine.DIALING_OUT_RESULT, 0 /* fail */, 0, 1292 mDialingOutDevice)); 1293 } 1294 } 1295 1296 @Override toString()1297 public String toString() { 1298 return "DialingOutTimeoutEvent[" + mDialingOutDevice + "]"; 1299 } 1300 } 1301 1302 /** 1303 * Dial an outgoing call as requested by the remote device 1304 * 1305 * @param fromDevice remote device that initiated this dial out action 1306 * @param dialNumber number to dial 1307 * @return true on successful dial out 1308 */ 1309 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber)1310 public boolean dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber) { 1311 synchronized (mStateMachines) { 1312 Log.i(TAG, "dialOutgoingCall: from " + fromDevice); 1313 if (!isOnStateMachineThread()) { 1314 Log.e(TAG, "dialOutgoingCall must be called from state machine thread"); 1315 return false; 1316 } 1317 if (mDialingOutTimeoutEvent != null) { 1318 Log.e(TAG, "dialOutgoingCall, already dialing by " + mDialingOutTimeoutEvent); 1319 return false; 1320 } 1321 if (isVirtualCallStarted()) { 1322 if (!stopScoUsingVirtualVoiceCall()) { 1323 Log.e(TAG, "dialOutgoingCall failed to stop current virtual call"); 1324 return false; 1325 } 1326 } 1327 if (!setActiveDevice(fromDevice)) { 1328 Log.e(TAG, "dialOutgoingCall failed to set active device to " + fromDevice); 1329 return false; 1330 } 1331 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1332 Uri.fromParts(PhoneAccount.SCHEME_TEL, dialNumber, null)); 1333 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1334 startActivity(intent); 1335 mDialingOutTimeoutEvent = new DialingOutTimeoutEvent(fromDevice); 1336 mStateMachinesThread.getThreadHandler() 1337 .postDelayed(mDialingOutTimeoutEvent, DIALING_OUT_TIMEOUT_MS); 1338 return true; 1339 } 1340 } 1341 1342 /** 1343 * Check if any connected headset has started dialing calls 1344 * 1345 * @return true if some device has started dialing calls 1346 */ 1347 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) hasDeviceInitiatedDialingOut()1348 public boolean hasDeviceInitiatedDialingOut() { 1349 synchronized (mStateMachines) { 1350 return mDialingOutTimeoutEvent != null; 1351 } 1352 } 1353 1354 class VoiceRecognitionTimeoutEvent implements Runnable { 1355 BluetoothDevice mVoiceRecognitionDevice; 1356 VoiceRecognitionTimeoutEvent(BluetoothDevice device)1357 VoiceRecognitionTimeoutEvent(BluetoothDevice device) { 1358 mVoiceRecognitionDevice = device; 1359 } 1360 1361 @Override run()1362 public void run() { 1363 synchronized (mStateMachines) { 1364 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1365 mSystemInterface.getVoiceRecognitionWakeLock().release(); 1366 } 1367 mVoiceRecognitionTimeoutEvent = null; 1368 doForStateMachine(mVoiceRecognitionDevice, stateMachine -> stateMachine.sendMessage( 1369 HeadsetStateMachine.VOICE_RECOGNITION_RESULT, 0 /* fail */, 0, 1370 mVoiceRecognitionDevice)); 1371 } 1372 } 1373 1374 @Override toString()1375 public String toString() { 1376 return "VoiceRecognitionTimeoutEvent[" + mVoiceRecognitionDevice + "]"; 1377 } 1378 } 1379 startVoiceRecognitionByHeadset(BluetoothDevice fromDevice)1380 boolean startVoiceRecognitionByHeadset(BluetoothDevice fromDevice) { 1381 synchronized (mStateMachines) { 1382 Log.i(TAG, "startVoiceRecognitionByHeadset: from " + fromDevice); 1383 // TODO(b/79660380): Workaround in case voice recognition was not terminated properly 1384 if (mVoiceRecognitionStarted) { 1385 boolean status = stopVoiceRecognition(mActiveDevice); 1386 Log.w(TAG, "startVoiceRecognitionByHeadset: voice recognition is still active, " 1387 + "just called stopVoiceRecognition, returned " + status + " on " 1388 + mActiveDevice + ", please try again"); 1389 mVoiceRecognitionStarted = false; 1390 return false; 1391 } 1392 if (fromDevice == null) { 1393 Log.e(TAG, "startVoiceRecognitionByHeadset: fromDevice is null"); 1394 return false; 1395 } 1396 if (!isAudioModeIdle()) { 1397 Log.w(TAG, "startVoiceRecognitionByHeadset: audio mode not idle, active device is " 1398 + mActiveDevice); 1399 return false; 1400 } 1401 // Audio should not be on when no audio mode is active 1402 if (isAudioOn()) { 1403 // Disconnect audio so that user can try later 1404 boolean status = disconnectAudio(); 1405 Log.w(TAG, "startVoiceRecognitionByHeadset: audio is still active, please wait for" 1406 + " audio to be disconnected, disconnectAudio() returned " + status 1407 + ", active device is " + mActiveDevice); 1408 return false; 1409 } 1410 // Do not start new request until the current one is finished or timeout 1411 if (mVoiceRecognitionTimeoutEvent != null) { 1412 Log.w(TAG, "startVoiceRecognitionByHeadset: failed request from " + fromDevice 1413 + ", already pending by " + mVoiceRecognitionTimeoutEvent); 1414 return false; 1415 } 1416 if (!setActiveDevice(fromDevice)) { 1417 Log.w(TAG, "startVoiceRecognitionByHeadset: failed to set " + fromDevice 1418 + " as active"); 1419 return false; 1420 } 1421 IDeviceIdleController deviceIdleController = IDeviceIdleController.Stub.asInterface( 1422 ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); 1423 if (deviceIdleController == null) { 1424 Log.w(TAG, "startVoiceRecognitionByHeadset: deviceIdleController is null, device=" 1425 + fromDevice); 1426 return false; 1427 } 1428 try { 1429 deviceIdleController.exitIdle("voice-command"); 1430 } catch (RemoteException e) { 1431 Log.w(TAG, 1432 "startVoiceRecognitionByHeadset: failed to exit idle, device=" + fromDevice 1433 + ", error=" + e.getMessage()); 1434 return false; 1435 } 1436 if (!mSystemInterface.activateVoiceRecognition()) { 1437 Log.w(TAG, "startVoiceRecognitionByHeadset: failed request from " + fromDevice); 1438 return false; 1439 } 1440 mVoiceRecognitionTimeoutEvent = new VoiceRecognitionTimeoutEvent(fromDevice); 1441 mStateMachinesThread.getThreadHandler() 1442 .postDelayed(mVoiceRecognitionTimeoutEvent, sStartVrTimeoutMs); 1443 if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1444 mSystemInterface.getVoiceRecognitionWakeLock().acquire(sStartVrTimeoutMs); 1445 } 1446 return true; 1447 } 1448 } 1449 stopVoiceRecognitionByHeadset(BluetoothDevice fromDevice)1450 boolean stopVoiceRecognitionByHeadset(BluetoothDevice fromDevice) { 1451 synchronized (mStateMachines) { 1452 Log.i(TAG, "stopVoiceRecognitionByHeadset: from " + fromDevice); 1453 if (!Objects.equals(fromDevice, mActiveDevice)) { 1454 Log.w(TAG, "stopVoiceRecognitionByHeadset: " + fromDevice 1455 + " is not active, active device is " + mActiveDevice); 1456 return false; 1457 } 1458 if (!mVoiceRecognitionStarted && mVoiceRecognitionTimeoutEvent == null) { 1459 Log.w(TAG, "stopVoiceRecognitionByHeadset: voice recognition not started, device=" 1460 + fromDevice); 1461 return false; 1462 } 1463 if (mVoiceRecognitionTimeoutEvent != null) { 1464 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1465 mSystemInterface.getVoiceRecognitionWakeLock().release(); 1466 } 1467 mStateMachinesThread.getThreadHandler() 1468 .removeCallbacks(mVoiceRecognitionTimeoutEvent); 1469 mVoiceRecognitionTimeoutEvent = null; 1470 } 1471 if (mVoiceRecognitionStarted) { 1472 if (!disconnectAudio()) { 1473 Log.w(TAG, "stopVoiceRecognitionByHeadset: failed to disconnect audio from " 1474 + fromDevice); 1475 } 1476 mVoiceRecognitionStarted = false; 1477 } 1478 if (!mSystemInterface.deactivateVoiceRecognition()) { 1479 Log.w(TAG, "stopVoiceRecognitionByHeadset: failed request from " + fromDevice); 1480 return false; 1481 } 1482 return true; 1483 } 1484 } 1485 phoneStateChanged(int numActive, int numHeld, int callState, String number, int type, String name, boolean isVirtualCall)1486 private void phoneStateChanged(int numActive, int numHeld, int callState, String number, 1487 int type, String name, boolean isVirtualCall) { 1488 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "Need MODIFY_PHONE_STATE permission"); 1489 synchronized (mStateMachines) { 1490 // Should stop all other audio mode in this case 1491 if ((numActive + numHeld) > 0 || callState != HeadsetHalConstants.CALL_STATE_IDLE) { 1492 if (!isVirtualCall && mVirtualCallStarted) { 1493 // stop virtual voice call if there is an incoming Telecom call update 1494 stopScoUsingVirtualVoiceCall(); 1495 } 1496 if (mVoiceRecognitionStarted) { 1497 // stop voice recognition if there is any incoming call 1498 stopVoiceRecognition(mActiveDevice); 1499 } 1500 } 1501 if (mDialingOutTimeoutEvent != null) { 1502 // Send result to state machine when dialing starts 1503 if (callState == HeadsetHalConstants.CALL_STATE_DIALING) { 1504 mStateMachinesThread.getThreadHandler() 1505 .removeCallbacks(mDialingOutTimeoutEvent); 1506 doForStateMachine(mDialingOutTimeoutEvent.mDialingOutDevice, 1507 stateMachine -> stateMachine.sendMessage( 1508 HeadsetStateMachine.DIALING_OUT_RESULT, 1 /* success */, 0, 1509 mDialingOutTimeoutEvent.mDialingOutDevice)); 1510 } else if (callState == HeadsetHalConstants.CALL_STATE_ACTIVE 1511 || callState == HeadsetHalConstants.CALL_STATE_IDLE) { 1512 // Clear the timeout event when the call is connected or disconnected 1513 if (!mStateMachinesThread.getThreadHandler() 1514 .hasCallbacks(mDialingOutTimeoutEvent)) { 1515 mDialingOutTimeoutEvent = null; 1516 } 1517 } 1518 } 1519 } 1520 mStateMachinesThread.getThreadHandler().post(() -> { 1521 boolean isCallIdleBefore = mSystemInterface.isCallIdle(); 1522 mSystemInterface.getHeadsetPhoneState().setNumActiveCall(numActive); 1523 mSystemInterface.getHeadsetPhoneState().setNumHeldCall(numHeld); 1524 mSystemInterface.getHeadsetPhoneState().setCallState(callState); 1525 // Suspend A2DP when call about is about to become active 1526 if (callState != HeadsetHalConstants.CALL_STATE_DISCONNECTED 1527 && !mSystemInterface.isCallIdle() && isCallIdleBefore) { 1528 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true"); 1529 } 1530 }); 1531 doForEachConnectedStateMachine( 1532 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED, 1533 new HeadsetCallState(numActive, numHeld, callState, number, type, name))); 1534 mStateMachinesThread.getThreadHandler().post(() -> { 1535 if (callState == HeadsetHalConstants.CALL_STATE_IDLE 1536 && mSystemInterface.isCallIdle() && !isAudioOn()) { 1537 // Resume A2DP when call ended and SCO is not connected 1538 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false"); 1539 } 1540 }); 1541 1542 } 1543 clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type)1544 private void clccResponse(int index, int direction, int status, int mode, boolean mpty, 1545 String number, int type) { 1546 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "Need MODIFY_PHONE_STATE permission"); 1547 doForEachConnectedStateMachine( 1548 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE, 1549 new HeadsetClccResponse(index, direction, status, mode, mpty, number, 1550 type))); 1551 } 1552 sendVendorSpecificResultCode(BluetoothDevice device, String command, String arg)1553 private boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, 1554 String arg) { 1555 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1556 synchronized (mStateMachines) { 1557 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1558 if (stateMachine == null) { 1559 Log.w(TAG, "sendVendorSpecificResultCode: device " + device 1560 + " was never connected/connecting"); 1561 return false; 1562 } 1563 int connectionState = stateMachine.getConnectionState(); 1564 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 1565 return false; 1566 } 1567 // Currently we support only "+ANDROID". 1568 if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) { 1569 Log.w(TAG, "Disallowed unsolicited result code command: " + command); 1570 return false; 1571 } 1572 stateMachine.sendMessage(HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE, 1573 new HeadsetVendorSpecificResultCode(device, command, arg)); 1574 } 1575 return true; 1576 } 1577 isInbandRingingEnabled()1578 boolean isInbandRingingEnabled() { 1579 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1580 return BluetoothHeadset.isInbandRingingSupported(this) && !SystemProperties.getBoolean( 1581 DISABLE_INBAND_RINGING_PROPERTY, false) && !mInbandRingingRuntimeDisable; 1582 } 1583 1584 /** 1585 * Called from {@link HeadsetStateMachine} in state machine thread when there is a connection 1586 * state change 1587 * 1588 * @param device remote device 1589 * @param fromState from which connection state is the change 1590 * @param toState to which connection state is the change 1591 */ 1592 @VisibleForTesting onConnectionStateChangedFromStateMachine(BluetoothDevice device, int fromState, int toState)1593 public void onConnectionStateChangedFromStateMachine(BluetoothDevice device, int fromState, 1594 int toState) { 1595 synchronized (mStateMachines) { 1596 List<BluetoothDevice> audioConnectableDevices = 1597 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 1598 if (fromState != BluetoothProfile.STATE_CONNECTED 1599 && toState == BluetoothProfile.STATE_CONNECTED) { 1600 if (audioConnectableDevices.size() > 1) { 1601 mInbandRingingRuntimeDisable = true; 1602 doForEachConnectedStateMachine( 1603 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, 1604 0)); 1605 } 1606 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET); 1607 } 1608 if (fromState != BluetoothProfile.STATE_DISCONNECTED 1609 && toState == BluetoothProfile.STATE_DISCONNECTED) { 1610 if (audioConnectableDevices.size() <= 1) { 1611 mInbandRingingRuntimeDisable = false; 1612 doForEachConnectedStateMachine( 1613 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, 1614 1)); 1615 } 1616 if (device.equals(mActiveDevice)) { 1617 setActiveDevice(null); 1618 } 1619 } 1620 } 1621 } 1622 1623 /** 1624 * Check if no audio mode is active 1625 * 1626 * @return false if virtual call, voice recognition, or Telecom call is active, true if all idle 1627 */ isAudioModeIdle()1628 private boolean isAudioModeIdle() { 1629 synchronized (mStateMachines) { 1630 if (mVoiceRecognitionStarted || mVirtualCallStarted || !mSystemInterface.isCallIdle()) { 1631 Log.i(TAG, "isAudioModeIdle: not idle, mVoiceRecognitionStarted=" 1632 + mVoiceRecognitionStarted + ", mVirtualCallStarted=" + mVirtualCallStarted 1633 + ", isCallIdle=" + mSystemInterface.isCallIdle()); 1634 return false; 1635 } 1636 return true; 1637 } 1638 } 1639 shouldCallAudioBeActive()1640 private boolean shouldCallAudioBeActive() { 1641 return mSystemInterface.isInCall() || (mSystemInterface.isRinging() 1642 && isInbandRingingEnabled()); 1643 } 1644 1645 /** 1646 * Only persist audio during active device switch when call audio is supposed to be active and 1647 * virtual call has not been started. Virtual call is ignored because AudioService and 1648 * applications should reconnect SCO during active device switch and forcing SCO connection 1649 * here will make AudioService think SCO is started externally instead of by one of its SCO 1650 * clients. 1651 * 1652 * @return true if call audio should be active and no virtual call is going on 1653 */ shouldPersistAudio()1654 private boolean shouldPersistAudio() { 1655 return !mVirtualCallStarted && shouldCallAudioBeActive(); 1656 } 1657 1658 /** 1659 * Called from {@link HeadsetStateMachine} in state machine thread when there is a audio 1660 * connection state change 1661 * 1662 * @param device remote device 1663 * @param fromState from which audio connection state is the change 1664 * @param toState to which audio connection state is the change 1665 */ 1666 @VisibleForTesting onAudioStateChangedFromStateMachine(BluetoothDevice device, int fromState, int toState)1667 public void onAudioStateChangedFromStateMachine(BluetoothDevice device, int fromState, 1668 int toState) { 1669 synchronized (mStateMachines) { 1670 if (toState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1671 if (fromState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1672 if (mActiveDevice != null && !mActiveDevice.equals(device) 1673 && shouldPersistAudio()) { 1674 if (!connectAudio(mActiveDevice)) { 1675 Log.w(TAG, "onAudioStateChangedFromStateMachine, failed to connect" 1676 + " audio to new " + "active device " + mActiveDevice 1677 + ", after " + device + " is disconnected from SCO"); 1678 } 1679 } 1680 } 1681 if (mVoiceRecognitionStarted) { 1682 if (!stopVoiceRecognitionByHeadset(device)) { 1683 Log.w(TAG, "onAudioStateChangedFromStateMachine: failed to stop voice " 1684 + "recognition"); 1685 } 1686 } 1687 if (mVirtualCallStarted) { 1688 if (!stopScoUsingVirtualVoiceCall()) { 1689 Log.w(TAG, "onAudioStateChangedFromStateMachine: failed to stop virtual " 1690 + "voice call"); 1691 } 1692 } 1693 // Unsuspend A2DP when SCO connection is gone and call state is idle 1694 if (mSystemInterface.isCallIdle()) { 1695 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false"); 1696 } 1697 } 1698 } 1699 } 1700 broadcastActiveDevice(BluetoothDevice device)1701 private void broadcastActiveDevice(BluetoothDevice device) { 1702 logD("broadcastActiveDevice: " + device); 1703 StatsLog.write(StatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED, BluetoothProfile.HEADSET, 1704 mAdapterService.obfuscateAddress(device)); 1705 Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); 1706 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1707 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1708 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1709 sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 1710 } 1711 1712 /** 1713 * Check whether it is OK to accept a headset connection from a remote device 1714 * 1715 * @param device remote device that initiates the connection 1716 * @return true if the connection is acceptable 1717 */ okToAcceptConnection(BluetoothDevice device)1718 public boolean okToAcceptConnection(BluetoothDevice device) { 1719 // Check if this is an incoming connection in Quiet mode. 1720 if (mAdapterService.isQuietModeEnabled()) { 1721 Log.w(TAG, "okToAcceptConnection: return false as quiet mode enabled"); 1722 return false; 1723 } 1724 // Check priority and accept or reject the connection. 1725 int priority = getPriority(device); 1726 int bondState = mAdapterService.getBondState(device); 1727 // Allow this connection only if the device is bonded. Any attempt to connect while 1728 // bonding would potentially lead to an unauthorized connection. 1729 if (bondState != BluetoothDevice.BOND_BONDED) { 1730 Log.w(TAG, "okToAcceptConnection: return false, bondState=" + bondState); 1731 return false; 1732 } else if (priority != BluetoothProfile.PRIORITY_UNDEFINED 1733 && priority != BluetoothProfile.PRIORITY_ON 1734 && priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) { 1735 // Otherwise, reject the connection if priority is not valid. 1736 Log.w(TAG, "okToAcceptConnection: return false, priority=" + priority); 1737 return false; 1738 } 1739 List<BluetoothDevice> connectingConnectedDevices = 1740 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 1741 if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) { 1742 Log.w(TAG, "Maximum number of connections " + mMaxHeadsetConnections 1743 + " was reached, rejecting connection from " + device); 1744 return false; 1745 } 1746 return true; 1747 } 1748 1749 /** 1750 * Checks if SCO should be connected at current system state 1751 * 1752 * @param device device for SCO to be connected 1753 * @return true if SCO is allowed to be connected 1754 */ isScoAcceptable(BluetoothDevice device)1755 public boolean isScoAcceptable(BluetoothDevice device) { 1756 synchronized (mStateMachines) { 1757 if (device == null || !device.equals(mActiveDevice)) { 1758 Log.w(TAG, "isScoAcceptable: rejected SCO since " + device 1759 + " is not the current active device " + mActiveDevice); 1760 return false; 1761 } 1762 if (mForceScoAudio) { 1763 return true; 1764 } 1765 if (!mAudioRouteAllowed) { 1766 Log.w(TAG, "isScoAcceptable: rejected SCO since audio route is not allowed"); 1767 return false; 1768 } 1769 if (mVoiceRecognitionStarted || mVirtualCallStarted) { 1770 return true; 1771 } 1772 if (shouldCallAudioBeActive()) { 1773 return true; 1774 } 1775 Log.w(TAG, "isScoAcceptable: rejected SCO, inCall=" + mSystemInterface.isInCall() 1776 + ", voiceRecognition=" + mVoiceRecognitionStarted + ", ringing=" 1777 + mSystemInterface.isRinging() + ", inbandRinging=" + isInbandRingingEnabled() 1778 + ", isVirtualCallStarted=" + mVirtualCallStarted); 1779 return false; 1780 } 1781 } 1782 1783 /** 1784 * Remove state machine in {@link #mStateMachines} for a {@link BluetoothDevice} 1785 * 1786 * @param device device whose state machine is to be removed. 1787 */ removeStateMachine(BluetoothDevice device)1788 void removeStateMachine(BluetoothDevice device) { 1789 synchronized (mStateMachines) { 1790 HeadsetStateMachine stateMachine = mStateMachines.get(device); 1791 if (stateMachine == null) { 1792 Log.w(TAG, "removeStateMachine(), " + device + " does not have a state machine"); 1793 return; 1794 } 1795 Log.i(TAG, "removeStateMachine(), removing state machine for device: " + device); 1796 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); 1797 mStateMachines.remove(device); 1798 } 1799 } 1800 isOnStateMachineThread()1801 private boolean isOnStateMachineThread() { 1802 final Looper myLooper = Looper.myLooper(); 1803 return myLooper != null && (mStateMachinesThread != null) && (myLooper.getThread().getId() 1804 == mStateMachinesThread.getId()); 1805 } 1806 1807 @Override dump(StringBuilder sb)1808 public void dump(StringBuilder sb) { 1809 synchronized (mStateMachines) { 1810 super.dump(sb); 1811 ProfileService.println(sb, "mMaxHeadsetConnections: " + mMaxHeadsetConnections); 1812 ProfileService.println(sb, "DefaultMaxHeadsetConnections: " 1813 + mAdapterService.getMaxConnectedAudioDevices()); 1814 ProfileService.println(sb, "mActiveDevice: " + mActiveDevice); 1815 ProfileService.println(sb, "isInbandRingingEnabled: " + isInbandRingingEnabled()); 1816 ProfileService.println(sb, 1817 "isInbandRingingSupported: " + BluetoothHeadset.isInbandRingingSupported(this)); 1818 ProfileService.println(sb, 1819 "mInbandRingingRuntimeDisable: " + mInbandRingingRuntimeDisable); 1820 ProfileService.println(sb, "mAudioRouteAllowed: " + mAudioRouteAllowed); 1821 ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 1822 ProfileService.println(sb, 1823 "mVoiceRecognitionTimeoutEvent: " + mVoiceRecognitionTimeoutEvent); 1824 ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted); 1825 ProfileService.println(sb, "mDialingOutTimeoutEvent: " + mDialingOutTimeoutEvent); 1826 ProfileService.println(sb, "mForceScoAudio: " + mForceScoAudio); 1827 ProfileService.println(sb, "mCreated: " + mCreated); 1828 ProfileService.println(sb, "mStarted: " + mStarted); 1829 ProfileService.println(sb, 1830 "AudioManager.isBluetoothScoOn(): " + mSystemInterface.getAudioManager() 1831 .isBluetoothScoOn()); 1832 ProfileService.println(sb, "Telecom.isInCall(): " + mSystemInterface.isInCall()); 1833 ProfileService.println(sb, "Telecom.isRinging(): " + mSystemInterface.isRinging()); 1834 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 1835 ProfileService.println(sb, 1836 "==== StateMachine for " + stateMachine.getDevice() + " ===="); 1837 stateMachine.dump(sb); 1838 } 1839 } 1840 } 1841 logD(String message)1842 private static void logD(String message) { 1843 if (DBG) { 1844 Log.d(TAG, message); 1845 } 1846 } 1847 } 1848