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