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