HeadsetStateMachine.java revision c303b43b314abb34284182eee839217469ac51cd
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/** 18 * Bluetooth Handset StateMachine 19 * (Disconnected) 20 * | ^ 21 * CONNECT | | DISCONNECTED 22 * V | 23 * (Pending) 24 * | ^ 25 * CONNECTED | | CONNECT 26 * V | 27 * (Connected) 28 * | ^ 29 * CONNECT_AUDIO | | DISCONNECT_AUDIO 30 * V | 31 * (AudioOn) 32 */ 33package com.android.bluetooth.hfp; 34 35import android.bluetooth.BluetoothAdapter; 36import android.bluetooth.BluetoothAssignedNumbers; 37import android.bluetooth.BluetoothDevice; 38import android.bluetooth.BluetoothHeadset; 39import android.bluetooth.BluetoothProfile; 40import android.bluetooth.BluetoothUuid; 41import android.bluetooth.IBluetoothHeadsetPhone; 42import android.content.ComponentName; 43import android.content.Context; 44import android.content.Intent; 45import android.content.ServiceConnection; 46import android.content.ActivityNotFoundException; 47import android.media.AudioManager; 48import android.net.Uri; 49import android.os.IBinder; 50import android.os.IDeviceIdleController; 51import android.os.Message; 52import android.os.ParcelUuid; 53import android.os.RemoteException; 54import android.os.ServiceManager; 55import android.os.PowerManager; 56import android.os.UserHandle; 57import android.os.PowerManager.WakeLock; 58import android.telephony.PhoneNumberUtils; 59import android.util.Log; 60import com.android.bluetooth.Utils; 61import com.android.bluetooth.btservice.AdapterService; 62import com.android.bluetooth.btservice.ProfileService; 63import com.android.internal.util.IState; 64import com.android.internal.util.State; 65import com.android.internal.util.StateMachine; 66import java.util.ArrayList; 67import java.util.HashMap; 68import java.util.List; 69import java.util.Map; 70import java.util.Set; 71import android.os.SystemProperties; 72 73final class HeadsetStateMachine extends StateMachine { 74 private static final String TAG = "HeadsetStateMachine"; 75 private static final boolean DBG = false; 76 // For Debugging only 77 private static int sRefCount = 0; 78 79 private static final String HEADSET_NAME = "bt_headset_name"; 80 private static final String HEADSET_NREC = "bt_headset_nrec"; 81 private static final String HEADSET_WBS = "bt_wbs"; 82 83 static final int CONNECT = 1; 84 static final int DISCONNECT = 2; 85 static final int CONNECT_AUDIO = 3; 86 static final int DISCONNECT_AUDIO = 4; 87 static final int VOICE_RECOGNITION_START = 5; 88 static final int VOICE_RECOGNITION_STOP = 6; 89 90 // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION 91 // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO 92 static final int INTENT_SCO_VOLUME_CHANGED = 7; 93 static final int SET_MIC_VOLUME = 8; 94 static final int CALL_STATE_CHANGED = 9; 95 static final int INTENT_BATTERY_CHANGED = 10; 96 static final int DEVICE_STATE_CHANGED = 11; 97 static final int SEND_CCLC_RESPONSE = 12; 98 static final int SEND_VENDOR_SPECIFIC_RESULT_CODE = 13; 99 100 static final int VIRTUAL_CALL_START = 14; 101 static final int VIRTUAL_CALL_STOP = 15; 102 103 static final int ENABLE_WBS = 16; 104 static final int DISABLE_WBS = 17; 105 106 static final int BIND_RESPONSE = 18; 107 108 private static final int STACK_EVENT = 101; 109 private static final int DIALING_OUT_TIMEOUT = 102; 110 private static final int START_VR_TIMEOUT = 103; 111 private static final int CLCC_RSP_TIMEOUT = 104; 112 113 private static final int CONNECT_TIMEOUT = 201; 114 115 private static final int DIALING_OUT_TIMEOUT_VALUE = 10000; 116 private static final int START_VR_TIMEOUT_VALUE = 5000; 117 private static final int CLCC_RSP_TIMEOUT_VALUE = 5000; 118 119 // Max number of HF connections at any time 120 private int max_hf_connections = 1; 121 122 private static final int NBS_CODEC = 1; 123 private static final int WBS_CODEC = 2; 124 125 // Keys are AT commands, and values are the company IDs. 126 private static final Map<String, Integer> VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID; 127 // Hash for storing the Audio Parameters like NREC for connected headsets 128 private HashMap<BluetoothDevice, HashMap> mHeadsetAudioParam = 129 new HashMap<BluetoothDevice, HashMap>(); 130 // Hash for storing the Remotedevice BRSF 131 private HashMap<BluetoothDevice, Integer> mHeadsetBrsf = 132 new HashMap<BluetoothDevice, Integer>(); 133 134 private static final ParcelUuid[] HEADSET_UUIDS = { 135 BluetoothUuid.HSP, BluetoothUuid.Handsfree, 136 }; 137 138 private Disconnected mDisconnected; 139 private Pending mPending; 140 private Connected mConnected; 141 private AudioOn mAudioOn; 142 // Multi HFP: add new class object 143 private MultiHFPending mMultiHFPending; 144 145 private HeadsetService mService; 146 private PowerManager mPowerManager; 147 private boolean mVirtualCallStarted = false; 148 private boolean mVoiceRecognitionStarted = false; 149 private boolean mWaitingForVoiceRecognition = false; 150 private WakeLock mStartVoiceRecognitionWakeLock; // held while waiting for voice recognition 151 152 private boolean mDialingOut = false; 153 private AudioManager mAudioManager; 154 private AtPhonebook mPhonebook; 155 156 private static Intent sVoiceCommandIntent; 157 158 private HeadsetPhoneState mPhoneState; 159 private int mAudioState; 160 private BluetoothAdapter mAdapter; 161 private IBluetoothHeadsetPhone mPhoneProxy; 162 private boolean mNativeAvailable; 163 164 // Indicates whether audio can be routed to the device. 165 private boolean mAudioRouteAllowed = true; 166 167 // mCurrentDevice is the device connected before the state changes 168 // mTargetDevice is the device to be connected 169 // mIncomingDevice is the device connecting to us, valid only in Pending state 170 // when mIncomingDevice is not null, both mCurrentDevice 171 // and mTargetDevice are null 172 // when either mCurrentDevice or mTargetDevice is not null, 173 // mIncomingDevice is null 174 // Stable states 175 // No connection, Disconnected state 176 // both mCurrentDevice and mTargetDevice are null 177 // Connected, Connected state 178 // mCurrentDevice is not null, mTargetDevice is null 179 // Interim states 180 // Connecting to a device, Pending 181 // mCurrentDevice is null, mTargetDevice is not null 182 // Disconnecting device, Connecting to new device 183 // Pending 184 // Both mCurrentDevice and mTargetDevice are not null 185 // Disconnecting device Pending 186 // mCurrentDevice is not null, mTargetDevice is null 187 // Incoming connections Pending 188 // Both mCurrentDevice and mTargetDevice are null 189 private BluetoothDevice mCurrentDevice = null; 190 private BluetoothDevice mTargetDevice = null; 191 private BluetoothDevice mIncomingDevice = null; 192 private BluetoothDevice mActiveScoDevice = null; 193 private BluetoothDevice mMultiDisconnectDevice = null; 194 195 // Multi HFP: Connected devices list holds all currently connected headsets 196 private ArrayList<BluetoothDevice> mConnectedDevicesList = new ArrayList<BluetoothDevice>(); 197 198 static { 199 classInitNative(); 200 201 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap<String, Integer>(); 202 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+XEVENT", BluetoothAssignedNumbers.PLANTRONICS); 203 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+ANDROID", BluetoothAssignedNumbers.GOOGLE); 204 } 205 206 private HeadsetStateMachine(HeadsetService context) { 207 super(TAG); 208 mService = context; 209 mVoiceRecognitionStarted = false; 210 mWaitingForVoiceRecognition = false; 211 212 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 213 mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock( 214 PowerManager.PARTIAL_WAKE_LOCK, TAG + ":VoiceRecognition"); 215 mStartVoiceRecognitionWakeLock.setReferenceCounted(false); 216 217 mDialingOut = false; 218 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 219 mPhonebook = new AtPhonebook(mService, this); 220 mPhoneState = new HeadsetPhoneState(context, this); 221 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 222 mAdapter = BluetoothAdapter.getDefaultAdapter(); 223 Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName()); 224 intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0)); 225 if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) { 226 Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service"); 227 } 228 229 String max_hfp_clients = SystemProperties.get("bt.max.hfpclient.connections"); 230 if (!max_hfp_clients.isEmpty() && (Integer.parseInt(max_hfp_clients) == 2)) { 231 max_hf_connections = Integer.parseInt(max_hfp_clients); 232 } 233 Log.d(TAG, "max_hf_connections = " + max_hf_connections); 234 initializeNative(max_hf_connections); 235 mNativeAvailable = true; 236 237 mDisconnected = new Disconnected(); 238 mPending = new Pending(); 239 mConnected = new Connected(); 240 mAudioOn = new AudioOn(); 241 // Multi HFP: initialise new class variable 242 mMultiHFPending = new MultiHFPending(); 243 244 if (sVoiceCommandIntent == null) { 245 sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND); 246 sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 247 } 248 249 addState(mDisconnected); 250 addState(mPending); 251 addState(mConnected); 252 addState(mAudioOn); 253 // Multi HFP: add State 254 addState(mMultiHFPending); 255 256 setInitialState(mDisconnected); 257 } 258 259 static HeadsetStateMachine make(HeadsetService context) { 260 Log.d(TAG, "make"); 261 HeadsetStateMachine hssm = new HeadsetStateMachine(context); 262 hssm.start(); 263 return hssm; 264 } 265 266 public void doQuit() { 267 quitNow(); 268 } 269 270 public void cleanup() { 271 if (mPhoneProxy != null) { 272 if (DBG) Log.d(TAG, "Unbinding service..."); 273 synchronized (mConnection) { 274 try { 275 mPhoneProxy = null; 276 mService.unbindService(mConnection); 277 } catch (Exception re) { 278 Log.e(TAG, "Error unbinding from IBluetoothHeadsetPhone", re); 279 } 280 } 281 } 282 if (mPhoneState != null) { 283 mPhoneState.listenForPhoneState(false); 284 mPhoneState.cleanup(); 285 } 286 if (mPhonebook != null) { 287 mPhonebook.cleanup(); 288 } 289 if (mHeadsetAudioParam != null) { 290 mHeadsetAudioParam.clear(); 291 } 292 if (mHeadsetBrsf != null) { 293 mHeadsetBrsf.clear(); 294 } 295 if (mConnectedDevicesList != null) { 296 mConnectedDevicesList.clear(); 297 } 298 if (mNativeAvailable) { 299 cleanupNative(); 300 mNativeAvailable = false; 301 } 302 } 303 304 public void dump(StringBuilder sb) { 305 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice); 306 ProfileService.println(sb, "mTargetDevice: " + mTargetDevice); 307 ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice); 308 ProfileService.println(sb, "mActiveScoDevice: " + mActiveScoDevice); 309 ProfileService.println(sb, "mMultiDisconnectDevice: " + mMultiDisconnectDevice); 310 ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted); 311 ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 312 ProfileService.println(sb, "mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition); 313 ProfileService.println(sb, "StateMachine: " + this.toString()); 314 ProfileService.println(sb, "mPhoneState: " + mPhoneState); 315 ProfileService.println(sb, "mAudioState: " + mAudioState); 316 } 317 318 private class Disconnected extends State { 319 @Override 320 public void enter() { 321 log("Enter Disconnected: " + getCurrentMessage().what + ", size: " 322 + mConnectedDevicesList.size()); 323 mPhonebook.resetAtState(); 324 mPhoneState.listenForPhoneState(false); 325 mVoiceRecognitionStarted = false; 326 mWaitingForVoiceRecognition = false; 327 } 328 329 @Override 330 public boolean processMessage(Message message) { 331 log("Disconnected process message: " + message.what + ", size: " 332 + mConnectedDevicesList.size()); 333 if (mConnectedDevicesList.size() != 0 || mTargetDevice != null 334 || mIncomingDevice != null) { 335 Log.e(TAG, "ERROR: mConnectedDevicesList is not empty," 336 + "target, or mIncomingDevice not null in Disconnected"); 337 return NOT_HANDLED; 338 } 339 340 boolean retValue = HANDLED; 341 switch (message.what) { 342 case CONNECT: 343 BluetoothDevice device = (BluetoothDevice) message.obj; 344 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 345 BluetoothProfile.STATE_DISCONNECTED); 346 347 if (!connectHfpNative(getByteAddress(device))) { 348 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 349 BluetoothProfile.STATE_CONNECTING); 350 break; 351 } 352 353 synchronized (HeadsetStateMachine.this) { 354 mTargetDevice = device; 355 transitionTo(mPending); 356 } 357 // TODO(BT) remove CONNECT_TIMEOUT when the stack 358 // sends back events consistently 359 Message m = obtainMessage(CONNECT_TIMEOUT); 360 m.obj = device; 361 sendMessageDelayed(m, 30000); 362 break; 363 case DISCONNECT: 364 // ignore 365 break; 366 case INTENT_BATTERY_CHANGED: 367 processIntentBatteryChanged((Intent) message.obj); 368 break; 369 case CALL_STATE_CHANGED: 370 processCallState((HeadsetCallState) message.obj, message.arg1 == 1); 371 break; 372 case STACK_EVENT: 373 StackEvent event = (StackEvent) message.obj; 374 if (DBG) { 375 log("event type: " + event.type); 376 } 377 switch (event.type) { 378 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 379 processConnectionEvent(event.valueInt, event.device); 380 break; 381 default: 382 Log.e(TAG, "Unexpected stack event: " + event.type); 383 break; 384 } 385 break; 386 default: 387 return NOT_HANDLED; 388 } 389 return retValue; 390 } 391 392 @Override 393 public void exit() { 394 log("Exit Disconnected: " + getCurrentMessage().what); 395 } 396 397 // in Disconnected state 398 private void processConnectionEvent(int state, BluetoothDevice device) { 399 Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device); 400 switch (state) { 401 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 402 Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device); 403 break; 404 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 405 if (okToConnect(device)) { 406 Log.i(TAG, "Incoming Hf accepted"); 407 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 408 BluetoothProfile.STATE_DISCONNECTED); 409 synchronized (HeadsetStateMachine.this) { 410 mIncomingDevice = device; 411 transitionTo(mPending); 412 } 413 } else { 414 Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device) 415 + " bondState=" + device.getBondState()); 416 // reject the connection and stay in Disconnected state itself 417 disconnectHfpNative(getByteAddress(device)); 418 // the other profile connection should be initiated 419 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 420 BluetoothProfile.STATE_DISCONNECTED); 421 } 422 break; 423 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 424 Log.w(TAG, "HFP Connected from Disconnected state"); 425 if (okToConnect(device)) { 426 Log.i(TAG, "Incoming Hf accepted"); 427 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 428 BluetoothProfile.STATE_DISCONNECTED); 429 synchronized (HeadsetStateMachine.this) { 430 if (!mConnectedDevicesList.contains(device)) { 431 mConnectedDevicesList.add(device); 432 Log.d(TAG, "device " + device.getAddress() 433 + " is adding in Disconnected state"); 434 } 435 mCurrentDevice = device; 436 transitionTo(mConnected); 437 } 438 configAudioParameters(device); 439 } else { 440 // reject the connection and stay in Disconnected state itself 441 Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device) 442 + " bondState=" + device.getBondState()); 443 disconnectHfpNative(getByteAddress(device)); 444 // the other profile connection should be initiated 445 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 446 BluetoothProfile.STATE_DISCONNECTED); 447 } 448 break; 449 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 450 Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device); 451 break; 452 default: 453 Log.e(TAG, "Incorrect state: " + state); 454 break; 455 } 456 } 457 } 458 459 private class Pending extends State { 460 @Override 461 public void enter() { 462 log("Enter Pending: " + getCurrentMessage().what); 463 } 464 465 @Override 466 public boolean processMessage(Message message) { 467 log("Pending process message: " + message.what + ", size: " 468 + mConnectedDevicesList.size()); 469 470 boolean retValue = HANDLED; 471 switch (message.what) { 472 case CONNECT: 473 case CONNECT_AUDIO: 474 deferMessage(message); 475 break; 476 case CONNECT_TIMEOUT: 477 onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, 478 getByteAddress(mTargetDevice)); 479 break; 480 case DISCONNECT: 481 BluetoothDevice device = (BluetoothDevice) message.obj; 482 if (mCurrentDevice != null && mTargetDevice != null 483 && mTargetDevice.equals(device)) { 484 // cancel connection to the mTargetDevice 485 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 486 BluetoothProfile.STATE_CONNECTING); 487 synchronized (HeadsetStateMachine.this) { 488 mTargetDevice = null; 489 } 490 } else { 491 deferMessage(message); 492 } 493 break; 494 case INTENT_BATTERY_CHANGED: 495 processIntentBatteryChanged((Intent) message.obj); 496 break; 497 case CALL_STATE_CHANGED: 498 processCallState((HeadsetCallState) message.obj, message.arg1 == 1); 499 break; 500 case STACK_EVENT: 501 StackEvent event = (StackEvent) message.obj; 502 if (DBG) { 503 log("event type: " + event.type); 504 } 505 switch (event.type) { 506 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 507 BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT); 508 if (device1 != null && device1.equals(event.device)) { 509 Log.d(TAG, "remove connect timeout for device = " + device1); 510 removeMessages(CONNECT_TIMEOUT); 511 } 512 processConnectionEvent(event.valueInt, event.device); 513 break; 514 515 case EVENT_TYPE_BIND: 516 processAtBind(event.valueString, event.device); 517 break; 518 519 case EVENT_TYPE_BIEV: 520 processAtBiev(event.valueInt, event.valueInt2, event.device); 521 break; 522 523 default: 524 Log.e(TAG, "Unexpected event: " + event.type); 525 break; 526 } 527 break; 528 default: 529 return NOT_HANDLED; 530 } 531 return retValue; 532 } 533 534 // in Pending state 535 private void processConnectionEvent(int state, BluetoothDevice device) { 536 Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device); 537 switch (state) { 538 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 539 if (mConnectedDevicesList.contains(device)) { 540 synchronized (HeadsetStateMachine.this) { 541 mConnectedDevicesList.remove(device); 542 mHeadsetAudioParam.remove(device); 543 mHeadsetBrsf.remove(device); 544 Log.d(TAG, "device " + device.getAddress() 545 + " is removed in Pending state"); 546 } 547 548 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 549 BluetoothProfile.STATE_DISCONNECTING); 550 synchronized (HeadsetStateMachine.this) { 551 mCurrentDevice = null; 552 } 553 554 processWBSEvent(0, device); /* disable WBS audio parameters */ 555 556 if (mTargetDevice != null) { 557 if (!connectHfpNative(getByteAddress(mTargetDevice))) { 558 broadcastConnectionState(mTargetDevice, 559 BluetoothProfile.STATE_DISCONNECTED, 560 BluetoothProfile.STATE_CONNECTING); 561 synchronized (HeadsetStateMachine.this) { 562 mTargetDevice = null; 563 transitionTo(mDisconnected); 564 } 565 } 566 } else { 567 synchronized (HeadsetStateMachine.this) { 568 mIncomingDevice = null; 569 if (mConnectedDevicesList.size() == 0) { 570 transitionTo(mDisconnected); 571 } else { 572 processMultiHFConnected(device); 573 } 574 } 575 } 576 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 577 // outgoing connection failed 578 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 579 BluetoothProfile.STATE_CONNECTING); 580 synchronized (HeadsetStateMachine.this) { 581 mTargetDevice = null; 582 if (mConnectedDevicesList.size() == 0) { 583 transitionTo(mDisconnected); 584 } else { 585 transitionTo(mConnected); 586 } 587 } 588 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 589 broadcastConnectionState(mIncomingDevice, 590 BluetoothProfile.STATE_DISCONNECTED, 591 BluetoothProfile.STATE_CONNECTING); 592 synchronized (HeadsetStateMachine.this) { 593 mIncomingDevice = null; 594 if (mConnectedDevicesList.size() == 0) { 595 transitionTo(mDisconnected); 596 } else { 597 transitionTo(mConnected); 598 } 599 } 600 } else { 601 Log.e(TAG, "Unknown device Disconnected: " + device); 602 } 603 break; 604 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 605 if (mConnectedDevicesList.contains(device)) { 606 // disconnection failed 607 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 608 BluetoothProfile.STATE_DISCONNECTING); 609 if (mTargetDevice != null) { 610 broadcastConnectionState(mTargetDevice, 611 BluetoothProfile.STATE_DISCONNECTED, 612 BluetoothProfile.STATE_CONNECTING); 613 } 614 synchronized (HeadsetStateMachine.this) { 615 mTargetDevice = null; 616 transitionTo(mConnected); 617 } 618 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 619 synchronized (HeadsetStateMachine.this) { 620 mCurrentDevice = device; 621 mConnectedDevicesList.add(device); 622 Log.d(TAG, 623 "device " + device.getAddress() + " is added in Pending state"); 624 mTargetDevice = null; 625 transitionTo(mConnected); 626 } 627 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 628 BluetoothProfile.STATE_CONNECTING); 629 configAudioParameters(device); 630 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 631 synchronized (HeadsetStateMachine.this) { 632 mCurrentDevice = device; 633 mConnectedDevicesList.add(device); 634 Log.d(TAG, 635 "device " + device.getAddress() + " is added in Pending state"); 636 mIncomingDevice = null; 637 transitionTo(mConnected); 638 } 639 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 640 BluetoothProfile.STATE_CONNECTING); 641 configAudioParameters(device); 642 } else { 643 Log.w(TAG, "Some other incoming HF connected in Pending state"); 644 if (okToConnect(device)) { 645 Log.i(TAG, "Incoming Hf accepted"); 646 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 647 BluetoothProfile.STATE_DISCONNECTED); 648 synchronized (HeadsetStateMachine.this) { 649 mCurrentDevice = device; 650 mConnectedDevicesList.add(device); 651 Log.d(TAG, "device " + device.getAddress() 652 + " is added in Pending state"); 653 } 654 configAudioParameters(device); 655 } else { 656 // reject the connection and stay in Pending state itself 657 Log.i(TAG, "Incoming Hf rejected. priority=" 658 + mService.getPriority(device) + " bondState=" 659 + device.getBondState()); 660 disconnectHfpNative(getByteAddress(device)); 661 } 662 } 663 break; 664 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 665 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 666 log("current device tries to connect back"); 667 // TODO(BT) ignore or reject 668 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 669 // The stack is connecting to target device or 670 // there is an incoming connection from the target device at the same time 671 // we already broadcasted the intent, doing nothing here 672 if (DBG) { 673 log("Stack and target device are connecting"); 674 } 675 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 676 Log.e(TAG, "Another connecting event on the incoming device"); 677 } else { 678 // We get an incoming connecting request while Pending 679 // TODO(BT) is stack handing this case? let's ignore it for now 680 log("Incoming connection while pending, ignore"); 681 } 682 break; 683 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 684 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 685 // we already broadcasted the intent, doing nothing here 686 if (DBG) { 687 log("stack is disconnecting mCurrentDevice"); 688 } 689 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 690 Log.e(TAG, "TargetDevice is getting disconnected"); 691 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 692 Log.e(TAG, "IncomingDevice is getting disconnected"); 693 } else { 694 Log.e(TAG, "Disconnecting unknow device: " + device); 695 } 696 break; 697 default: 698 Log.e(TAG, "Incorrect state: " + state); 699 break; 700 } 701 } 702 703 private void processMultiHFConnected(BluetoothDevice device) { 704 log("Pending state: processMultiHFConnected"); 705 /* Assign the current activedevice again if the disconnected 706 device equals to the current active device*/ 707 if (mCurrentDevice != null && mCurrentDevice.equals(device)) { 708 transitionTo(mConnected); 709 int deviceSize = mConnectedDevicesList.size(); 710 mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1); 711 } else { 712 // The disconnected device is not current active device 713 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) 714 transitionTo(mAudioOn); 715 else 716 transitionTo(mConnected); 717 } 718 log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice); 719 log("Pending state: processMultiHFConnected ," 720 + "fake broadcasting for mCurrentDevice"); 721 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 722 BluetoothProfile.STATE_DISCONNECTED); 723 } 724 } 725 726 private class Connected extends State { 727 @Override 728 public void enter() { 729 // Remove pending connection attempts that were deferred during the pending 730 // state. This is to prevent auto connect attempts from disconnecting 731 // devices that previously successfully connected. 732 // TODO: This needs to check for multiple HFP connections, once supported... 733 removeDeferredMessages(CONNECT); 734 735 log("Enter Connected: " + getCurrentMessage().what + ", size: " 736 + mConnectedDevicesList.size()); 737 // start phone state listener here so that the CIND response as part of SLC can be 738 // responded to, correctly. 739 // we may enter Connected from Disconnected/Pending/AudioOn. listenForPhoneState 740 // internally handles multiple calls to start listen 741 mPhoneState.listenForPhoneState(true); 742 } 743 744 @Override 745 public boolean processMessage(Message message) { 746 log("Connected process message: " + message.what + ", size: " 747 + mConnectedDevicesList.size()); 748 if (DBG) { 749 if (mConnectedDevicesList.size() == 0) { 750 log("ERROR: mConnectedDevicesList is empty in Connected"); 751 return NOT_HANDLED; 752 } 753 } 754 755 boolean retValue = HANDLED; 756 switch (message.what) { 757 case CONNECT: { 758 BluetoothDevice device = (BluetoothDevice) message.obj; 759 if (device == null) { 760 break; 761 } 762 763 if (mConnectedDevicesList.contains(device)) { 764 Log.e(TAG, "ERROR: Connect received for already connected device, Ignore"); 765 break; 766 } 767 768 if (mConnectedDevicesList.size() >= max_hf_connections) { 769 BluetoothDevice DisconnectConnectedDevice = null; 770 IState CurrentAudioState = getCurrentState(); 771 Log.d(TAG, "Reach to max size, disconnect one of them first"); 772 /* TODO: Disconnect based on CoD */ 773 DisconnectConnectedDevice = mConnectedDevicesList.get(0); 774 775 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 776 BluetoothProfile.STATE_DISCONNECTED); 777 778 if (!disconnectHfpNative(getByteAddress(DisconnectConnectedDevice))) { 779 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 780 BluetoothProfile.STATE_CONNECTING); 781 break; 782 } else { 783 broadcastConnectionState(DisconnectConnectedDevice, 784 BluetoothProfile.STATE_DISCONNECTING, 785 BluetoothProfile.STATE_CONNECTED); 786 } 787 788 synchronized (HeadsetStateMachine.this) { 789 mTargetDevice = device; 790 if (max_hf_connections == 1) { 791 transitionTo(mPending); 792 } else { 793 mMultiDisconnectDevice = DisconnectConnectedDevice; 794 transitionTo(mMultiHFPending); 795 } 796 DisconnectConnectedDevice = null; 797 } 798 } else if (mConnectedDevicesList.size() < max_hf_connections) { 799 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 800 BluetoothProfile.STATE_DISCONNECTED); 801 if (!connectHfpNative(getByteAddress(device))) { 802 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 803 BluetoothProfile.STATE_CONNECTING); 804 break; 805 } 806 synchronized (HeadsetStateMachine.this) { 807 mTargetDevice = device; 808 // Transtion to MultiHFPending state for Multi HF connection 809 transitionTo(mMultiHFPending); 810 } 811 } 812 Message m = obtainMessage(CONNECT_TIMEOUT); 813 m.obj = device; 814 sendMessageDelayed(m, 30000); 815 } break; 816 case DISCONNECT: { 817 BluetoothDevice device = (BluetoothDevice) message.obj; 818 if (!mConnectedDevicesList.contains(device)) { 819 break; 820 } 821 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 822 BluetoothProfile.STATE_CONNECTED); 823 if (!disconnectHfpNative(getByteAddress(device))) { 824 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 825 BluetoothProfile.STATE_DISCONNECTED); 826 break; 827 } 828 829 if (mConnectedDevicesList.size() > 1) { 830 mMultiDisconnectDevice = device; 831 transitionTo(mMultiHFPending); 832 } else { 833 transitionTo(mPending); 834 } 835 } break; 836 case CONNECT_AUDIO: { 837 BluetoothDevice device = mCurrentDevice; 838 if (!isScoAcceptable()) { 839 Log.w(TAG,"No Active/Held call, MO call setup, not allowing SCO"); 840 break; 841 } 842 // TODO(BT) when failure, broadcast audio connecting to disconnected intent 843 // check if device matches mCurrentDevice 844 if (mActiveScoDevice != null) { 845 log("connectAudioNative in Connected; mActiveScoDevice is not null"); 846 device = mActiveScoDevice; 847 } 848 log("connectAudioNative in Connected for device = " + device); 849 connectAudioNative(getByteAddress(device)); 850 } break; 851 case VOICE_RECOGNITION_START: 852 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED); 853 break; 854 case VOICE_RECOGNITION_STOP: 855 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); 856 break; 857 case CALL_STATE_CHANGED: 858 processCallState((HeadsetCallState) message.obj, message.arg1 == 1); 859 break; 860 case INTENT_BATTERY_CHANGED: 861 processIntentBatteryChanged((Intent) message.obj); 862 break; 863 case DEVICE_STATE_CHANGED: 864 processDeviceStateChanged((HeadsetDeviceState) message.obj); 865 break; 866 case SEND_CCLC_RESPONSE: 867 processSendClccResponse((HeadsetClccResponse) message.obj); 868 break; 869 case CLCC_RSP_TIMEOUT: { 870 BluetoothDevice device = (BluetoothDevice) message.obj; 871 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 872 } break; 873 case SEND_VENDOR_SPECIFIC_RESULT_CODE: 874 processSendVendorSpecificResultCode( 875 (HeadsetVendorSpecificResultCode) message.obj); 876 break; 877 case DIALING_OUT_TIMEOUT: { 878 BluetoothDevice device = (BluetoothDevice) message.obj; 879 if (mDialingOut) { 880 mDialingOut = false; 881 atResponseCodeNative( 882 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 883 } 884 } break; 885 case VIRTUAL_CALL_START: 886 initiateScoUsingVirtualVoiceCall(); 887 break; 888 case VIRTUAL_CALL_STOP: 889 terminateScoUsingVirtualVoiceCall(); 890 break; 891 case ENABLE_WBS: { 892 BluetoothDevice device = (BluetoothDevice) message.obj; 893 configureWBSNative(getByteAddress(device), WBS_CODEC); 894 break; 895 } 896 case DISABLE_WBS: { 897 BluetoothDevice device = (BluetoothDevice) message.obj; 898 configureWBSNative(getByteAddress(device), NBS_CODEC); 899 break; 900 } 901 case BIND_RESPONSE: { 902 BluetoothDevice device = (BluetoothDevice) message.obj; 903 bindResponseNative( 904 (int) message.arg1, message.arg2 == 1, getByteAddress(device)); 905 } break; 906 case START_VR_TIMEOUT: { 907 BluetoothDevice device = (BluetoothDevice) message.obj; 908 if (mWaitingForVoiceRecognition) { 909 device = (BluetoothDevice) message.obj; 910 mWaitingForVoiceRecognition = false; 911 Log.e(TAG, "Timeout waiting for voice recognition to start"); 912 atResponseCodeNative( 913 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 914 } 915 } break; 916 case STACK_EVENT: 917 StackEvent event = (StackEvent) message.obj; 918 if (DBG) { 919 log("event type: " + event.type + "event device : " + event.device); 920 } 921 switch (event.type) { 922 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 923 processConnectionEvent(event.valueInt, event.device); 924 break; 925 case EVENT_TYPE_AUDIO_STATE_CHANGED: 926 processAudioEvent(event.valueInt, event.device); 927 break; 928 case EVENT_TYPE_VR_STATE_CHANGED: 929 processVrEvent(event.valueInt, event.device); 930 break; 931 case EVENT_TYPE_ANSWER_CALL: 932 // TODO(BT) could answer call happen on Connected state? 933 processAnswerCall(event.device); 934 break; 935 case EVENT_TYPE_HANGUP_CALL: 936 // TODO(BT) could hangup call happen on Connected state? 937 processHangupCall(event.device); 938 break; 939 case EVENT_TYPE_VOLUME_CHANGED: 940 processVolumeEvent(event.valueInt, event.valueInt2, event.device); 941 break; 942 case EVENT_TYPE_DIAL_CALL: 943 processDialCall(event.valueString, event.device); 944 break; 945 case EVENT_TYPE_SEND_DTMF: 946 processSendDtmf(event.valueInt, event.device); 947 break; 948 case EVENT_TYPE_NOICE_REDUCTION: 949 processNoiceReductionEvent(event.valueInt, event.device); 950 break; 951 case EVENT_TYPE_WBS: 952 Log.d(TAG, "EVENT_TYPE_WBS codec is " + event.valueInt); 953 processWBSEvent(event.valueInt, event.device); 954 break; 955 case EVENT_TYPE_AT_CHLD: 956 processAtChld(event.valueInt, event.device); 957 break; 958 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 959 processSubscriberNumberRequest(event.device); 960 break; 961 case EVENT_TYPE_AT_CIND: 962 processAtCind(event.device); 963 break; 964 case EVENT_TYPE_AT_COPS: 965 processAtCops(event.device); 966 break; 967 case EVENT_TYPE_AT_CLCC: 968 processAtClcc(event.device); 969 break; 970 case EVENT_TYPE_UNKNOWN_AT: 971 processUnknownAt(event.valueString, event.device); 972 break; 973 case EVENT_TYPE_KEY_PRESSED: 974 processKeyPressed(event.device); 975 break; 976 case EVENT_TYPE_BIND: 977 processAtBind(event.valueString, event.device); 978 break; 979 case EVENT_TYPE_BIEV: 980 processAtBiev(event.valueInt, event.valueInt2, event.device); 981 break; 982 default: 983 Log.e(TAG, "Unknown stack event: " + event.type); 984 break; 985 } 986 break; 987 default: 988 return NOT_HANDLED; 989 } 990 return retValue; 991 } 992 993 // in Connected state 994 private void processConnectionEvent(int state, BluetoothDevice device) { 995 Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device); 996 switch (state) { 997 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 998 if (mConnectedDevicesList.contains(device)) { 999 processWBSEvent(0, device); /* disable WBS audio parameters */ 1000 synchronized (HeadsetStateMachine.this) { 1001 mConnectedDevicesList.remove(device); 1002 mHeadsetAudioParam.remove(device); 1003 mHeadsetBrsf.remove(device); 1004 Log.d(TAG, "device " + device.getAddress() 1005 + " is removed in Connected state"); 1006 1007 if (mConnectedDevicesList.size() == 0) { 1008 mCurrentDevice = null; 1009 transitionTo(mDisconnected); 1010 } else { 1011 processMultiHFConnected(device); 1012 } 1013 } 1014 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1015 BluetoothProfile.STATE_CONNECTED); 1016 } else { 1017 Log.e(TAG, "Disconnected from unknown device: " + device); 1018 } 1019 break; 1020 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 1021 processSlcConnected(); 1022 break; 1023 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 1024 if (mConnectedDevicesList.contains(device)) { 1025 mIncomingDevice = null; 1026 mTargetDevice = null; 1027 break; 1028 } 1029 Log.w(TAG, "HFP to be Connected in Connected state"); 1030 if (okToConnect(device) 1031 && (mConnectedDevicesList.size() < max_hf_connections)) { 1032 Log.i(TAG, "Incoming Hf accepted"); 1033 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1034 BluetoothProfile.STATE_DISCONNECTED); 1035 synchronized (HeadsetStateMachine.this) { 1036 if (!mConnectedDevicesList.contains(device)) { 1037 mCurrentDevice = device; 1038 mConnectedDevicesList.add(device); 1039 Log.d(TAG, "device " + device.getAddress() 1040 + " is added in Connected state"); 1041 } 1042 transitionTo(mConnected); 1043 } 1044 configAudioParameters(device); 1045 } else { 1046 // reject the connection and stay in Connected state itself 1047 Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device) 1048 + " bondState=" + device.getBondState()); 1049 disconnectHfpNative(getByteAddress(device)); 1050 } 1051 break; 1052 default: 1053 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1054 break; 1055 } 1056 } 1057 1058 // in Connected state 1059 private void processAudioEvent(int state, BluetoothDevice device) { 1060 if (!mConnectedDevicesList.contains(device)) { 1061 Log.e(TAG, "Audio changed on disconnected device: " + device); 1062 return; 1063 } 1064 1065 switch (state) { 1066 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 1067 if (!isScoAcceptable()) { 1068 Log.e(TAG, "Audio Connected without any listener"); 1069 disconnectAudioNative(getByteAddress(device)); 1070 break; 1071 } 1072 1073 // TODO(BT) should I save the state for next broadcast as the prevState? 1074 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED; 1075 setAudioParameters(device); /*Set proper Audio Paramters.*/ 1076 mAudioManager.setBluetoothScoOn(true); 1077 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED, 1078 BluetoothHeadset.STATE_AUDIO_CONNECTING); 1079 mActiveScoDevice = device; 1080 transitionTo(mAudioOn); 1081 break; 1082 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 1083 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING; 1084 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING, 1085 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1086 break; 1087 // TODO(BT) process other states 1088 default: 1089 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1090 break; 1091 } 1092 } 1093 1094 private void processSlcConnected() { 1095 if (mPhoneProxy != null) { 1096 try { 1097 mPhoneProxy.queryPhoneState(); 1098 } catch (RemoteException e) { 1099 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1100 } 1101 } else { 1102 Log.e(TAG, "Handsfree phone proxy null for query phone state"); 1103 } 1104 } 1105 1106 private void processMultiHFConnected(BluetoothDevice device) { 1107 log("Connect state: processMultiHFConnected"); 1108 if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) { 1109 log("mActiveScoDevice is disconnected, setting it to null"); 1110 mActiveScoDevice = null; 1111 } 1112 /* Assign the current activedevice again if the disconnected 1113 device equals to the current active device */ 1114 if (mCurrentDevice != null && mCurrentDevice.equals(device)) { 1115 transitionTo(mConnected); 1116 int deviceSize = mConnectedDevicesList.size(); 1117 mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1); 1118 } else { 1119 // The disconnected device is not current active device 1120 transitionTo(mConnected); 1121 } 1122 log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice); 1123 log("Connect state: processMultiHFConnected ," 1124 + "fake broadcasting for mCurrentDevice"); 1125 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 1126 BluetoothProfile.STATE_DISCONNECTED); 1127 } 1128 } 1129 1130 private class AudioOn extends State { 1131 @Override 1132 public void enter() { 1133 log("Enter AudioOn: " + getCurrentMessage().what + ", size: " 1134 + mConnectedDevicesList.size()); 1135 } 1136 1137 @Override 1138 public boolean processMessage(Message message) { 1139 log("AudioOn process message: " + message.what + ", size: " 1140 + mConnectedDevicesList.size()); 1141 if (DBG) { 1142 if (mConnectedDevicesList.size() == 0) { 1143 log("ERROR: mConnectedDevicesList is empty in AudioOn"); 1144 return NOT_HANDLED; 1145 } 1146 } 1147 1148 boolean retValue = HANDLED; 1149 switch (message.what) { 1150 case CONNECT: { 1151 BluetoothDevice device = (BluetoothDevice) message.obj; 1152 if (device == null) { 1153 break; 1154 } 1155 1156 if (mConnectedDevicesList.contains(device)) { 1157 break; 1158 } 1159 1160 if (max_hf_connections == 1) { 1161 deferMessage(obtainMessage(DISCONNECT, mCurrentDevice)); 1162 deferMessage(obtainMessage(CONNECT, device)); 1163 if (disconnectAudioNative(getByteAddress(mCurrentDevice))) { 1164 Log.d(TAG, "Disconnecting SCO audio for device = " + mCurrentDevice); 1165 } else { 1166 Log.e(TAG, "disconnectAudioNative failed"); 1167 } 1168 break; 1169 } 1170 1171 if (mConnectedDevicesList.size() >= max_hf_connections) { 1172 BluetoothDevice DisconnectConnectedDevice = null; 1173 IState CurrentAudioState = getCurrentState(); 1174 Log.d(TAG, "Reach to max size, disconnect " 1175 + "one of them first"); 1176 DisconnectConnectedDevice = mConnectedDevicesList.get(0); 1177 1178 if (mActiveScoDevice.equals(DisconnectConnectedDevice)) { 1179 DisconnectConnectedDevice = mConnectedDevicesList.get(1); 1180 } 1181 1182 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 1183 BluetoothProfile.STATE_DISCONNECTED); 1184 1185 if (!disconnectHfpNative(getByteAddress(DisconnectConnectedDevice))) { 1186 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1187 BluetoothProfile.STATE_CONNECTING); 1188 break; 1189 } else { 1190 broadcastConnectionState(DisconnectConnectedDevice, 1191 BluetoothProfile.STATE_DISCONNECTING, 1192 BluetoothProfile.STATE_CONNECTED); 1193 } 1194 1195 synchronized (HeadsetStateMachine.this) { 1196 mTargetDevice = device; 1197 mMultiDisconnectDevice = DisconnectConnectedDevice; 1198 transitionTo(mMultiHFPending); 1199 DisconnectConnectedDevice = null; 1200 } 1201 } else if (mConnectedDevicesList.size() < max_hf_connections) { 1202 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 1203 BluetoothProfile.STATE_DISCONNECTED); 1204 if (!connectHfpNative(getByteAddress(device))) { 1205 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1206 BluetoothProfile.STATE_CONNECTING); 1207 break; 1208 } 1209 synchronized (HeadsetStateMachine.this) { 1210 mTargetDevice = device; 1211 // Transtion to MultilHFPending state for Multi handsfree connection 1212 transitionTo(mMultiHFPending); 1213 } 1214 } 1215 Message m = obtainMessage(CONNECT_TIMEOUT); 1216 m.obj = device; 1217 sendMessageDelayed(m, 30000); 1218 } break; 1219 case CONNECT_TIMEOUT: 1220 onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, 1221 getByteAddress(mTargetDevice)); 1222 break; 1223 case DISCONNECT: { 1224 BluetoothDevice device = (BluetoothDevice) message.obj; 1225 if (!mConnectedDevicesList.contains(device)) { 1226 break; 1227 } 1228 if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) { 1229 // The disconnected device is active SCO device 1230 Log.d(TAG, "AudioOn, the disconnected device" 1231 + "is active SCO device"); 1232 deferMessage(obtainMessage(DISCONNECT, message.obj)); 1233 // Disconnect BT SCO first 1234 if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) { 1235 log("Disconnecting SCO audio"); 1236 } else { 1237 // if disconnect BT SCO failed, transition to mConnected state 1238 transitionTo(mConnected); 1239 } 1240 } else { 1241 /* Do not disconnect BT SCO if the disconnected 1242 device is not active SCO device */ 1243 Log.d(TAG, "AudioOn, the disconnected device" 1244 + "is not active SCO device"); 1245 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 1246 BluetoothProfile.STATE_CONNECTED); 1247 // Should be still in AudioOn state 1248 if (!disconnectHfpNative(getByteAddress(device))) { 1249 Log.w(TAG, "AudioOn, disconnect device failed"); 1250 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1251 BluetoothProfile.STATE_DISCONNECTING); 1252 break; 1253 } 1254 /* Transtion to MultiHFPending state for Multi 1255 handsfree connection */ 1256 if (mConnectedDevicesList.size() > 1) { 1257 mMultiDisconnectDevice = device; 1258 transitionTo(mMultiHFPending); 1259 } 1260 } 1261 } break; 1262 case DISCONNECT_AUDIO: 1263 if (mActiveScoDevice != null) { 1264 if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) { 1265 log("Disconnecting SCO audio for device = " + mActiveScoDevice); 1266 } else { 1267 Log.e(TAG, "disconnectAudioNative failed" 1268 + "for device = " + mActiveScoDevice); 1269 } 1270 } 1271 break; 1272 case VOICE_RECOGNITION_START: 1273 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED); 1274 break; 1275 case VOICE_RECOGNITION_STOP: 1276 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); 1277 break; 1278 case INTENT_SCO_VOLUME_CHANGED: 1279 if (mActiveScoDevice != null) { 1280 processIntentScoVolume((Intent) message.obj, mActiveScoDevice); 1281 } 1282 break; 1283 case CALL_STATE_CHANGED: 1284 processCallState((HeadsetCallState) message.obj, message.arg1 == 1); 1285 break; 1286 case INTENT_BATTERY_CHANGED: 1287 processIntentBatteryChanged((Intent) message.obj); 1288 break; 1289 case DEVICE_STATE_CHANGED: 1290 processDeviceStateChanged((HeadsetDeviceState) message.obj); 1291 break; 1292 case SEND_CCLC_RESPONSE: 1293 processSendClccResponse((HeadsetClccResponse) message.obj); 1294 break; 1295 case CLCC_RSP_TIMEOUT: { 1296 BluetoothDevice device = (BluetoothDevice) message.obj; 1297 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 1298 break; 1299 } 1300 case SEND_VENDOR_SPECIFIC_RESULT_CODE: 1301 processSendVendorSpecificResultCode( 1302 (HeadsetVendorSpecificResultCode) message.obj); 1303 break; 1304 1305 case VIRTUAL_CALL_START: 1306 initiateScoUsingVirtualVoiceCall(); 1307 break; 1308 case VIRTUAL_CALL_STOP: 1309 terminateScoUsingVirtualVoiceCall(); 1310 break; 1311 1312 case DIALING_OUT_TIMEOUT: { 1313 if (mDialingOut) { 1314 BluetoothDevice device = (BluetoothDevice) message.obj; 1315 mDialingOut = false; 1316 atResponseCodeNative( 1317 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 1318 } 1319 break; 1320 } 1321 case START_VR_TIMEOUT: { 1322 if (mWaitingForVoiceRecognition) { 1323 BluetoothDevice device = (BluetoothDevice) message.obj; 1324 mWaitingForVoiceRecognition = false; 1325 Log.e(TAG, "Timeout waiting for voice recognition" 1326 + "to start"); 1327 atResponseCodeNative( 1328 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 1329 } 1330 break; 1331 } 1332 case STACK_EVENT: 1333 StackEvent event = (StackEvent) message.obj; 1334 if (DBG) { 1335 log("event type: " + event.type); 1336 } 1337 switch (event.type) { 1338 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 1339 BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT); 1340 if (device1 != null && device1.equals(event.device)) { 1341 Log.d(TAG, "remove connect timeout for device = " + device1); 1342 removeMessages(CONNECT_TIMEOUT); 1343 } 1344 processConnectionEvent(event.valueInt, event.device); 1345 break; 1346 case EVENT_TYPE_AUDIO_STATE_CHANGED: 1347 processAudioEvent(event.valueInt, event.device); 1348 break; 1349 case EVENT_TYPE_VR_STATE_CHANGED: 1350 processVrEvent(event.valueInt, event.device); 1351 break; 1352 case EVENT_TYPE_ANSWER_CALL: 1353 processAnswerCall(event.device); 1354 break; 1355 case EVENT_TYPE_HANGUP_CALL: 1356 processHangupCall(event.device); 1357 break; 1358 case EVENT_TYPE_VOLUME_CHANGED: 1359 processVolumeEvent(event.valueInt, event.valueInt2, event.device); 1360 break; 1361 case EVENT_TYPE_DIAL_CALL: 1362 processDialCall(event.valueString, event.device); 1363 break; 1364 case EVENT_TYPE_SEND_DTMF: 1365 processSendDtmf(event.valueInt, event.device); 1366 break; 1367 case EVENT_TYPE_NOICE_REDUCTION: 1368 processNoiceReductionEvent(event.valueInt, event.device); 1369 break; 1370 case EVENT_TYPE_AT_CHLD: 1371 processAtChld(event.valueInt, event.device); 1372 break; 1373 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 1374 processSubscriberNumberRequest(event.device); 1375 break; 1376 case EVENT_TYPE_AT_CIND: 1377 processAtCind(event.device); 1378 break; 1379 case EVENT_TYPE_AT_COPS: 1380 processAtCops(event.device); 1381 break; 1382 case EVENT_TYPE_AT_CLCC: 1383 processAtClcc(event.device); 1384 break; 1385 case EVENT_TYPE_UNKNOWN_AT: 1386 processUnknownAt(event.valueString, event.device); 1387 break; 1388 case EVENT_TYPE_KEY_PRESSED: 1389 processKeyPressed(event.device); 1390 break; 1391 case EVENT_TYPE_BIND: 1392 processAtBind(event.valueString, event.device); 1393 break; 1394 case EVENT_TYPE_BIEV: 1395 processAtBiev(event.valueInt, event.valueInt2, event.device); 1396 break; 1397 default: 1398 Log.e(TAG, "Unknown stack event: " + event.type); 1399 break; 1400 } 1401 break; 1402 default: 1403 return NOT_HANDLED; 1404 } 1405 return retValue; 1406 } 1407 1408 // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this 1409 private void processConnectionEvent(int state, BluetoothDevice device) { 1410 Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device); 1411 switch (state) { 1412 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 1413 if (mConnectedDevicesList.contains(device)) { 1414 if (mActiveScoDevice != null && mActiveScoDevice.equals(device) 1415 && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1416 processAudioEvent(HeadsetHalConstants.AUDIO_STATE_DISCONNECTED, device); 1417 } 1418 1419 synchronized (HeadsetStateMachine.this) { 1420 mConnectedDevicesList.remove(device); 1421 mHeadsetAudioParam.remove(device); 1422 mHeadsetBrsf.remove(device); 1423 Log.d(TAG, "device " + device.getAddress() 1424 + " is removed in AudioOn state"); 1425 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1426 BluetoothProfile.STATE_CONNECTED); 1427 processWBSEvent(0, device); /* disable WBS audio parameters */ 1428 if (mConnectedDevicesList.size() == 0) { 1429 transitionTo(mDisconnected); 1430 } else { 1431 processMultiHFConnected(device); 1432 } 1433 } 1434 } else { 1435 Log.e(TAG, "Disconnected from unknown device: " + device); 1436 } 1437 break; 1438 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 1439 processSlcConnected(); 1440 break; 1441 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 1442 if (mConnectedDevicesList.contains(device)) { 1443 mIncomingDevice = null; 1444 mTargetDevice = null; 1445 break; 1446 } 1447 Log.w(TAG, "HFP to be Connected in AudioOn state"); 1448 if (okToConnect(device) 1449 && (mConnectedDevicesList.size() < max_hf_connections)) { 1450 Log.i(TAG, "Incoming Hf accepted"); 1451 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1452 BluetoothProfile.STATE_DISCONNECTED); 1453 synchronized (HeadsetStateMachine.this) { 1454 if (!mConnectedDevicesList.contains(device)) { 1455 mCurrentDevice = device; 1456 mConnectedDevicesList.add(device); 1457 Log.d(TAG, "device " + device.getAddress() 1458 + " is added in AudioOn state"); 1459 } 1460 } 1461 configAudioParameters(device); 1462 } else { 1463 // reject the connection and stay in Connected state itself 1464 Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device) 1465 + " bondState=" + device.getBondState()); 1466 disconnectHfpNative(getByteAddress(device)); 1467 } 1468 break; 1469 default: 1470 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1471 break; 1472 } 1473 } 1474 1475 // in AudioOn state 1476 private void processAudioEvent(int state, BluetoothDevice device) { 1477 if (!mConnectedDevicesList.contains(device)) { 1478 Log.e(TAG, "Audio changed on disconnected device: " + device); 1479 return; 1480 } 1481 1482 switch (state) { 1483 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1484 if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1485 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 1486 mAudioManager.setBluetoothScoOn(false); 1487 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 1488 BluetoothHeadset.STATE_AUDIO_CONNECTED); 1489 } 1490 transitionTo(mConnected); 1491 break; 1492 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1493 // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset? 1494 // broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING, 1495 // BluetoothHeadset.STATE_AUDIO_CONNECTED); 1496 break; 1497 default: 1498 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1499 break; 1500 } 1501 } 1502 1503 private void processSlcConnected() { 1504 if (mPhoneProxy != null) { 1505 try { 1506 mPhoneProxy.queryPhoneState(); 1507 } catch (RemoteException e) { 1508 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1509 } 1510 } else { 1511 Log.e(TAG, "Handsfree phone proxy null for query phone state"); 1512 } 1513 } 1514 1515 private void processIntentScoVolume(Intent intent, BluetoothDevice device) { 1516 int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 1517 if (mPhoneState.getSpeakerVolume() != volumeValue) { 1518 mPhoneState.setSpeakerVolume(volumeValue); 1519 setVolumeNative( 1520 HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue, getByteAddress(device)); 1521 } 1522 } 1523 1524 private void processMultiHFConnected(BluetoothDevice device) { 1525 log("AudioOn state: processMultiHFConnected"); 1526 /* Assign the current activedevice again if the disconnected 1527 device equals to the current active device */ 1528 if (mCurrentDevice != null && mCurrentDevice.equals(device)) { 1529 int deviceSize = mConnectedDevicesList.size(); 1530 mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1); 1531 } 1532 if (mAudioState != BluetoothHeadset.STATE_AUDIO_CONNECTED) transitionTo(mConnected); 1533 1534 log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice); 1535 log("AudioOn state: processMultiHFConnected ," 1536 + "fake broadcasting for mCurrentDevice"); 1537 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 1538 BluetoothProfile.STATE_DISCONNECTED); 1539 } 1540 } 1541 1542 /* Add MultiHFPending state when atleast 1 HS is connected 1543 and disconnect/connect new HS */ 1544 private class MultiHFPending extends State { 1545 @Override 1546 public void enter() { 1547 log("Enter MultiHFPending: " + getCurrentMessage().what + ", size: " 1548 + mConnectedDevicesList.size()); 1549 } 1550 1551 @Override 1552 public boolean processMessage(Message message) { 1553 log("MultiHFPending process message: " + message.what + ", size: " 1554 + mConnectedDevicesList.size()); 1555 1556 boolean retValue = HANDLED; 1557 switch (message.what) { 1558 case CONNECT: 1559 deferMessage(message); 1560 break; 1561 1562 case CONNECT_AUDIO: 1563 if (mCurrentDevice != null) { 1564 connectAudioNative(getByteAddress(mCurrentDevice)); 1565 } 1566 break; 1567 case CONNECT_TIMEOUT: 1568 onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, 1569 getByteAddress(mTargetDevice)); 1570 break; 1571 1572 case DISCONNECT_AUDIO: 1573 if (mActiveScoDevice != null) { 1574 if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) { 1575 Log.d(TAG, "MultiHFPending, Disconnecting SCO audio for " 1576 + mActiveScoDevice); 1577 } else { 1578 Log.e(TAG, "disconnectAudioNative failed" 1579 + "for device = " + mActiveScoDevice); 1580 } 1581 } 1582 break; 1583 case DISCONNECT: 1584 BluetoothDevice device = (BluetoothDevice) message.obj; 1585 if (mConnectedDevicesList.contains(device) && mTargetDevice != null 1586 && mTargetDevice.equals(device)) { 1587 // cancel connection to the mTargetDevice 1588 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1589 BluetoothProfile.STATE_CONNECTING); 1590 synchronized (HeadsetStateMachine.this) { 1591 mTargetDevice = null; 1592 } 1593 } else { 1594 deferMessage(message); 1595 } 1596 break; 1597 case VOICE_RECOGNITION_START: 1598 device = (BluetoothDevice) message.obj; 1599 if (mConnectedDevicesList.contains(device)) { 1600 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED); 1601 } 1602 break; 1603 case VOICE_RECOGNITION_STOP: 1604 device = (BluetoothDevice) message.obj; 1605 if (mConnectedDevicesList.contains(device)) { 1606 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); 1607 } 1608 break; 1609 case INTENT_SCO_VOLUME_CHANGED: 1610 if (mActiveScoDevice != null) { 1611 processIntentScoVolume((Intent) message.obj, mActiveScoDevice); 1612 } 1613 break; 1614 case INTENT_BATTERY_CHANGED: 1615 processIntentBatteryChanged((Intent) message.obj); 1616 break; 1617 case CALL_STATE_CHANGED: 1618 processCallState((HeadsetCallState) message.obj, message.arg1 == 1); 1619 break; 1620 case DEVICE_STATE_CHANGED: 1621 processDeviceStateChanged((HeadsetDeviceState) message.obj); 1622 break; 1623 case SEND_CCLC_RESPONSE: 1624 processSendClccResponse((HeadsetClccResponse) message.obj); 1625 break; 1626 case CLCC_RSP_TIMEOUT: { 1627 device = (BluetoothDevice) message.obj; 1628 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 1629 } break; 1630 case DIALING_OUT_TIMEOUT: 1631 if (mDialingOut) { 1632 device = (BluetoothDevice) message.obj; 1633 mDialingOut = false; 1634 atResponseCodeNative( 1635 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 1636 } 1637 break; 1638 case VIRTUAL_CALL_START: 1639 device = (BluetoothDevice) message.obj; 1640 if (mConnectedDevicesList.contains(device)) { 1641 initiateScoUsingVirtualVoiceCall(); 1642 } 1643 break; 1644 case VIRTUAL_CALL_STOP: 1645 device = (BluetoothDevice) message.obj; 1646 if (mConnectedDevicesList.contains(device)) { 1647 terminateScoUsingVirtualVoiceCall(); 1648 } 1649 break; 1650 case START_VR_TIMEOUT: 1651 if (mWaitingForVoiceRecognition) { 1652 device = (BluetoothDevice) message.obj; 1653 mWaitingForVoiceRecognition = false; 1654 Log.e(TAG, "Timeout waiting for voice" 1655 + "recognition to start"); 1656 atResponseCodeNative( 1657 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 1658 } 1659 break; 1660 case STACK_EVENT: 1661 StackEvent event = (StackEvent) message.obj; 1662 if (DBG) { 1663 log("event type: " + event.type); 1664 } 1665 switch (event.type) { 1666 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 1667 BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT); 1668 if (device1 != null && device1.equals(event.device)) { 1669 Log.d(TAG, "remove connect timeout for device = " + device1); 1670 removeMessages(CONNECT_TIMEOUT); 1671 } 1672 processConnectionEvent(event.valueInt, event.device); 1673 break; 1674 case EVENT_TYPE_AUDIO_STATE_CHANGED: 1675 processAudioEvent(event.valueInt, event.device); 1676 break; 1677 case EVENT_TYPE_VR_STATE_CHANGED: 1678 processVrEvent(event.valueInt, event.device); 1679 break; 1680 case EVENT_TYPE_ANSWER_CALL: 1681 // TODO(BT) could answer call happen on Connected state? 1682 processAnswerCall(event.device); 1683 break; 1684 case EVENT_TYPE_HANGUP_CALL: 1685 // TODO(BT) could hangup call happen on Connected state? 1686 processHangupCall(event.device); 1687 break; 1688 case EVENT_TYPE_VOLUME_CHANGED: 1689 processVolumeEvent(event.valueInt, event.valueInt2, event.device); 1690 break; 1691 case EVENT_TYPE_DIAL_CALL: 1692 processDialCall(event.valueString, event.device); 1693 break; 1694 case EVENT_TYPE_SEND_DTMF: 1695 processSendDtmf(event.valueInt, event.device); 1696 break; 1697 case EVENT_TYPE_NOICE_REDUCTION: 1698 processNoiceReductionEvent(event.valueInt, event.device); 1699 break; 1700 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 1701 processSubscriberNumberRequest(event.device); 1702 break; 1703 case EVENT_TYPE_AT_CIND: 1704 processAtCind(event.device); 1705 break; 1706 case EVENT_TYPE_AT_CHLD: 1707 processAtChld(event.valueInt, event.device); 1708 break; 1709 case EVENT_TYPE_AT_COPS: 1710 processAtCops(event.device); 1711 break; 1712 case EVENT_TYPE_AT_CLCC: 1713 processAtClcc(event.device); 1714 break; 1715 case EVENT_TYPE_UNKNOWN_AT: 1716 processUnknownAt(event.valueString, event.device); 1717 break; 1718 case EVENT_TYPE_KEY_PRESSED: 1719 processKeyPressed(event.device); 1720 break; 1721 case EVENT_TYPE_BIND: 1722 processAtBind(event.valueString, event.device); 1723 break; 1724 case EVENT_TYPE_BIEV: 1725 processAtBiev(event.valueInt, event.valueInt2, event.device); 1726 break; 1727 default: 1728 Log.e(TAG, "Unexpected event: " + event.type); 1729 break; 1730 } 1731 break; 1732 default: 1733 return NOT_HANDLED; 1734 } 1735 return retValue; 1736 } 1737 1738 // in MultiHFPending state 1739 private void processConnectionEvent(int state, BluetoothDevice device) { 1740 Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device); 1741 switch (state) { 1742 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 1743 if (mConnectedDevicesList.contains(device)) { 1744 if (mMultiDisconnectDevice != null 1745 && mMultiDisconnectDevice.equals(device)) { 1746 mMultiDisconnectDevice = null; 1747 1748 synchronized (HeadsetStateMachine.this) { 1749 mConnectedDevicesList.remove(device); 1750 mHeadsetAudioParam.remove(device); 1751 mHeadsetBrsf.remove(device); 1752 Log.d(TAG, "device " + device.getAddress() 1753 + " is removed in MultiHFPending state"); 1754 broadcastConnectionState(device, 1755 BluetoothProfile.STATE_DISCONNECTED, 1756 BluetoothProfile.STATE_DISCONNECTING); 1757 } 1758 1759 if (mTargetDevice != null) { 1760 if (!connectHfpNative(getByteAddress(mTargetDevice))) { 1761 broadcastConnectionState(mTargetDevice, 1762 BluetoothProfile.STATE_DISCONNECTED, 1763 BluetoothProfile.STATE_CONNECTING); 1764 synchronized (HeadsetStateMachine.this) { 1765 mTargetDevice = null; 1766 if (mConnectedDevicesList.size() == 0) { 1767 // Should be not in this state since it has at least 1768 // one HF connected in MultiHFPending state 1769 Log.d(TAG, 1770 "Should be not in this state, error handling"); 1771 transitionTo(mDisconnected); 1772 } else { 1773 processMultiHFConnected(device); 1774 } 1775 } 1776 } 1777 } else { 1778 synchronized (HeadsetStateMachine.this) { 1779 mIncomingDevice = null; 1780 if (mConnectedDevicesList.size() == 0) { 1781 transitionTo(mDisconnected); 1782 } else { 1783 processMultiHFConnected(device); 1784 } 1785 } 1786 } 1787 } else { 1788 /* Another HF disconnected when one HF is connecting */ 1789 synchronized (HeadsetStateMachine.this) { 1790 mConnectedDevicesList.remove(device); 1791 mHeadsetAudioParam.remove(device); 1792 mHeadsetBrsf.remove(device); 1793 Log.d(TAG, "device " + device.getAddress() 1794 + " is removed in MultiHFPending state"); 1795 } 1796 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1797 BluetoothProfile.STATE_CONNECTED); 1798 } 1799 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 1800 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 1801 BluetoothProfile.STATE_CONNECTING); 1802 synchronized (HeadsetStateMachine.this) { 1803 mTargetDevice = null; 1804 if (mConnectedDevicesList.size() == 0) { 1805 transitionTo(mDisconnected); 1806 } else { 1807 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) 1808 transitionTo(mAudioOn); 1809 else 1810 transitionTo(mConnected); 1811 } 1812 } 1813 } else { 1814 Log.e(TAG, "Unknown device Disconnected: " + device); 1815 } 1816 break; 1817 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 1818 /* Outgoing disconnection for device failed */ 1819 if (mConnectedDevicesList.contains(device)) { 1820 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1821 BluetoothProfile.STATE_DISCONNECTING); 1822 if (mTargetDevice != null) { 1823 broadcastConnectionState(mTargetDevice, 1824 BluetoothProfile.STATE_DISCONNECTED, 1825 BluetoothProfile.STATE_CONNECTING); 1826 } 1827 synchronized (HeadsetStateMachine.this) { 1828 mTargetDevice = null; 1829 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) 1830 transitionTo(mAudioOn); 1831 else 1832 transitionTo(mConnected); 1833 } 1834 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 1835 synchronized (HeadsetStateMachine.this) { 1836 mCurrentDevice = device; 1837 mConnectedDevicesList.add(device); 1838 Log.d(TAG, "device " + device.getAddress() 1839 + " is added in MultiHFPending state"); 1840 mTargetDevice = null; 1841 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) 1842 transitionTo(mAudioOn); 1843 else 1844 transitionTo(mConnected); 1845 } 1846 1847 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1848 BluetoothProfile.STATE_CONNECTING); 1849 configAudioParameters(device); 1850 } else { 1851 Log.w(TAG, "Some other incoming HF connected" 1852 + "in Multi Pending state"); 1853 if (okToConnect(device) 1854 && (mConnectedDevicesList.size() < max_hf_connections)) { 1855 Log.i(TAG, "Incoming Hf accepted"); 1856 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1857 BluetoothProfile.STATE_DISCONNECTED); 1858 synchronized (HeadsetStateMachine.this) { 1859 if (!mConnectedDevicesList.contains(device)) { 1860 mCurrentDevice = device; 1861 mConnectedDevicesList.add(device); 1862 Log.d(TAG, "device " + device.getAddress() 1863 + " is added in MultiHFPending state"); 1864 } 1865 } 1866 configAudioParameters(device); 1867 } else { 1868 // reject the connection and stay in Pending state itself 1869 Log.i(TAG, "Incoming Hf rejected. priority=" 1870 + mService.getPriority(device) + " bondState=" 1871 + device.getBondState()); 1872 disconnectHfpNative(getByteAddress(device)); 1873 } 1874 } 1875 break; 1876 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 1877 processSlcConnected(); 1878 break; 1879 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 1880 if (mConnectedDevicesList.contains(device)) { 1881 Log.e(TAG, "current device tries to connect back"); 1882 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 1883 if (DBG) { 1884 log("Stack and target device are connecting"); 1885 } 1886 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 1887 Log.e(TAG, "Another connecting event on" 1888 + "the incoming device"); 1889 } 1890 break; 1891 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 1892 if (mConnectedDevicesList.contains(device)) { 1893 if (DBG) { 1894 log("stack is disconnecting mCurrentDevice"); 1895 } 1896 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 1897 Log.e(TAG, "TargetDevice is getting disconnected"); 1898 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 1899 Log.e(TAG, "IncomingDevice is getting disconnected"); 1900 } else { 1901 Log.e(TAG, "Disconnecting unknow device: " + device); 1902 } 1903 break; 1904 default: 1905 Log.e(TAG, "Incorrect state: " + state); 1906 break; 1907 } 1908 } 1909 1910 private void processAudioEvent(int state, BluetoothDevice device) { 1911 if (!mConnectedDevicesList.contains(device)) { 1912 Log.e(TAG, "Audio changed on disconnected device: " + device); 1913 return; 1914 } 1915 1916 switch (state) { 1917 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 1918 if (!isScoAcceptable()) { 1919 Log.e(TAG, "Audio Connected without any listener"); 1920 disconnectAudioNative(getByteAddress(device)); 1921 break; 1922 } 1923 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED; 1924 setAudioParameters(device); /* Set proper Audio Parameters. */ 1925 mAudioManager.setBluetoothScoOn(true); 1926 mActiveScoDevice = device; 1927 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED, 1928 BluetoothHeadset.STATE_AUDIO_CONNECTING); 1929 /* The state should be still in MultiHFPending state when 1930 audio connected since other device is still connecting/ 1931 disconnecting */ 1932 break; 1933 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 1934 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING; 1935 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING, 1936 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1937 break; 1938 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1939 if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1940 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 1941 mAudioManager.setBluetoothScoOn(false); 1942 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 1943 BluetoothHeadset.STATE_AUDIO_CONNECTED); 1944 } 1945 /* The state should be still in MultiHFPending state when audio 1946 disconnected since other device is still connecting/ 1947 disconnecting */ 1948 break; 1949 1950 default: 1951 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1952 break; 1953 } 1954 } 1955 1956 private void processSlcConnected() { 1957 if (mPhoneProxy != null) { 1958 try { 1959 mPhoneProxy.queryPhoneState(); 1960 } catch (RemoteException e) { 1961 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1962 } 1963 } else { 1964 Log.e(TAG, "Handsfree phone proxy null for query phone state"); 1965 } 1966 } 1967 1968 private void processMultiHFConnected(BluetoothDevice device) { 1969 log("MultiHFPending state: processMultiHFConnected"); 1970 if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) { 1971 log("mActiveScoDevice is disconnected, setting it to null"); 1972 mActiveScoDevice = null; 1973 } 1974 /* Assign the current activedevice again if the disconnected 1975 device equals to the current active device */ 1976 if (mCurrentDevice != null && mCurrentDevice.equals(device)) { 1977 int deviceSize = mConnectedDevicesList.size(); 1978 mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1); 1979 } 1980 // The disconnected device is not current active device 1981 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) 1982 transitionTo(mAudioOn); 1983 else 1984 transitionTo(mConnected); 1985 log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice); 1986 log("MultiHFPending state: processMultiHFConnected ," 1987 + "fake broadcasting for mCurrentDevice"); 1988 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 1989 BluetoothProfile.STATE_DISCONNECTED); 1990 } 1991 1992 private void processIntentScoVolume(Intent intent, BluetoothDevice device) { 1993 int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 1994 if (mPhoneState.getSpeakerVolume() != volumeValue) { 1995 mPhoneState.setSpeakerVolume(volumeValue); 1996 setVolumeNative( 1997 HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue, getByteAddress(device)); 1998 } 1999 } 2000 } 2001 2002 private ServiceConnection mConnection = new ServiceConnection() { 2003 public void onServiceConnected(ComponentName className, IBinder service) { 2004 if (DBG) Log.d(TAG, "Proxy object connected"); 2005 mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service); 2006 } 2007 2008 public void onServiceDisconnected(ComponentName className) { 2009 if (DBG) Log.d(TAG, "Proxy object disconnected"); 2010 mPhoneProxy = null; 2011 } 2012 }; 2013 2014 // HFP Connection state of the device could be changed by the state machine 2015 // in separate thread while this method is executing. 2016 int getConnectionState(BluetoothDevice device) { 2017 if (getCurrentState() == mDisconnected) { 2018 if (DBG) Log.d(TAG, "currentState is Disconnected"); 2019 return BluetoothProfile.STATE_DISCONNECTED; 2020 } 2021 2022 synchronized (this) { 2023 IState currentState = getCurrentState(); 2024 if (DBG) Log.d(TAG, "currentState = " + currentState); 2025 if (currentState == mPending) { 2026 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 2027 return BluetoothProfile.STATE_CONNECTING; 2028 } 2029 if (mConnectedDevicesList.contains(device)) { 2030 return BluetoothProfile.STATE_DISCONNECTING; 2031 } 2032 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 2033 return BluetoothProfile.STATE_CONNECTING; // incoming connection 2034 } 2035 return BluetoothProfile.STATE_DISCONNECTED; 2036 } 2037 2038 if (currentState == mMultiHFPending) { 2039 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 2040 return BluetoothProfile.STATE_CONNECTING; 2041 } 2042 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 2043 return BluetoothProfile.STATE_CONNECTING; // incoming connection 2044 } 2045 if (mConnectedDevicesList.contains(device)) { 2046 if ((mMultiDisconnectDevice != null) 2047 && (!mMultiDisconnectDevice.equals(device))) { 2048 // The device is still connected 2049 return BluetoothProfile.STATE_CONNECTED; 2050 } 2051 return BluetoothProfile.STATE_DISCONNECTING; 2052 } 2053 return BluetoothProfile.STATE_DISCONNECTED; 2054 } 2055 2056 if (currentState == mConnected || currentState == mAudioOn) { 2057 if (mConnectedDevicesList.contains(device)) { 2058 return BluetoothProfile.STATE_CONNECTED; 2059 } 2060 return BluetoothProfile.STATE_DISCONNECTED; 2061 } else { 2062 Log.e(TAG, "Bad currentState: " + currentState); 2063 return BluetoothProfile.STATE_DISCONNECTED; 2064 } 2065 } 2066 } 2067 2068 List<BluetoothDevice> getConnectedDevices() { 2069 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 2070 synchronized (this) { 2071 for (int i = 0; i < mConnectedDevicesList.size(); i++) 2072 devices.add(mConnectedDevicesList.get(i)); 2073 } 2074 2075 return devices; 2076 } 2077 2078 boolean isAudioOn() { 2079 return (getCurrentState() == mAudioOn); 2080 } 2081 2082 boolean isAudioConnected(BluetoothDevice device) { 2083 synchronized (this) { 2084 /* Additional check for audio state included for the case when PhoneApp queries 2085 Bluetooth Audio state, before we receive the close event from the stack for the 2086 sco disconnect issued in AudioOn state. This was causing a mismatch in the 2087 Incall screen UI. */ 2088 2089 if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device) 2090 && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 2091 return true; 2092 } 2093 } 2094 return false; 2095 } 2096 2097 public void setAudioRouteAllowed(boolean allowed) { 2098 mAudioRouteAllowed = allowed; 2099 } 2100 2101 public boolean getAudioRouteAllowed() { 2102 return mAudioRouteAllowed; 2103 } 2104 2105 int getAudioState(BluetoothDevice device) { 2106 synchronized (this) { 2107 if (mConnectedDevicesList.size() == 0) { 2108 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 2109 } 2110 } 2111 return mAudioState; 2112 } 2113 2114 private void processVrEvent(int state, BluetoothDevice device) { 2115 if (device == null) { 2116 Log.w(TAG, "processVrEvent device is null"); 2117 return; 2118 } 2119 Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " 2120 + mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " 2121 + mWaitingForVoiceRecognition + " isInCall: " + isInCall()); 2122 if (state == HeadsetHalConstants.VR_STATE_STARTED) { 2123 if (!isVirtualCallInProgress() && !isInCall()) { 2124 IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface( 2125 ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); 2126 if (dic != null) { 2127 try { 2128 dic.exitIdle("voice-command"); 2129 } catch (RemoteException e) { 2130 } 2131 } 2132 try { 2133 mService.startActivity(sVoiceCommandIntent); 2134 } catch (ActivityNotFoundException e) { 2135 atResponseCodeNative( 2136 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2137 return; 2138 } 2139 expectVoiceRecognition(device); 2140 } else { 2141 // send error response if call is ongoing 2142 atResponseCodeNative( 2143 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2144 return; 2145 } 2146 } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) { 2147 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) { 2148 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device)); 2149 mVoiceRecognitionStarted = false; 2150 mWaitingForVoiceRecognition = false; 2151 if (!isInCall() && (mActiveScoDevice != null)) { 2152 disconnectAudioNative(getByteAddress(mActiveScoDevice)); 2153 mAudioManager.setParameters("A2dpSuspended=false"); 2154 } 2155 } else { 2156 atResponseCodeNative( 2157 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2158 } 2159 } else { 2160 Log.e(TAG, "Bad Voice Recognition state: " + state); 2161 } 2162 } 2163 2164 private void processLocalVrEvent(int state) { 2165 BluetoothDevice device = null; 2166 if (state == HeadsetHalConstants.VR_STATE_STARTED) { 2167 boolean needAudio = true; 2168 if (mVoiceRecognitionStarted || isInCall()) { 2169 Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() 2170 + " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 2171 return; 2172 } 2173 mVoiceRecognitionStarted = true; 2174 2175 if (mWaitingForVoiceRecognition) { 2176 device = getDeviceForMessage(START_VR_TIMEOUT); 2177 if (device == null) return; 2178 2179 Log.d(TAG, "Voice recognition started successfully"); 2180 mWaitingForVoiceRecognition = false; 2181 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device)); 2182 removeMessages(START_VR_TIMEOUT); 2183 } else { 2184 Log.d(TAG, "Voice recognition started locally"); 2185 needAudio = startVoiceRecognitionNative(getByteAddress(mCurrentDevice)); 2186 if (mCurrentDevice != null) device = mCurrentDevice; 2187 } 2188 2189 if (needAudio && !isAudioOn()) { 2190 Log.d(TAG, "Initiating audio connection for Voice Recognition"); 2191 // At this stage, we need to be sure that AVDTP is not streaming. This is needed 2192 // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in 2193 // streaming state while a SCO connection is established. 2194 // This is needed for VoiceDial scenario alone and not for 2195 // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE 2196 // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed. 2197 // Whereas for VoiceDial we want to activate the SCO connection but we are still 2198 // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream 2199 mAudioManager.setParameters("A2dpSuspended=true"); 2200 if (device != null) { 2201 connectAudioNative(getByteAddress(device)); 2202 } else { 2203 Log.e(TAG, "device not found for VR"); 2204 } 2205 } 2206 2207 if (mStartVoiceRecognitionWakeLock.isHeld()) { 2208 mStartVoiceRecognitionWakeLock.release(); 2209 } 2210 } else { 2211 Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " 2212 + mVoiceRecognitionStarted + " mWaitingForVoiceRecognition: " 2213 + mWaitingForVoiceRecognition); 2214 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) { 2215 mVoiceRecognitionStarted = false; 2216 mWaitingForVoiceRecognition = false; 2217 2218 if (stopVoiceRecognitionNative(getByteAddress(mCurrentDevice)) && !isInCall() 2219 && mActiveScoDevice != null) { 2220 disconnectAudioNative(getByteAddress(mActiveScoDevice)); 2221 mAudioManager.setParameters("A2dpSuspended=false"); 2222 } 2223 } 2224 } 2225 } 2226 2227 private synchronized void expectVoiceRecognition(BluetoothDevice device) { 2228 mWaitingForVoiceRecognition = true; 2229 Message m = obtainMessage(START_VR_TIMEOUT); 2230 m.obj = getMatchingDevice(device); 2231 sendMessageDelayed(m, START_VR_TIMEOUT_VALUE); 2232 2233 if (!mStartVoiceRecognitionWakeLock.isHeld()) { 2234 mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE); 2235 } 2236 } 2237 2238 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 2239 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 2240 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 2241 int connectionState; 2242 synchronized (this) { 2243 for (BluetoothDevice device : bondedDevices) { 2244 ParcelUuid[] featureUuids = device.getUuids(); 2245 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 2246 continue; 2247 } 2248 connectionState = getConnectionState(device); 2249 for (int i = 0; i < states.length; i++) { 2250 if (connectionState == states[i]) { 2251 deviceList.add(device); 2252 } 2253 } 2254 } 2255 } 2256 return deviceList; 2257 } 2258 2259 private BluetoothDevice getDeviceForMessage(int what) { 2260 if (what == CONNECT_TIMEOUT) { 2261 log("getDeviceForMessage: returning mTargetDevice for what=" + what); 2262 return mTargetDevice; 2263 } 2264 if (mConnectedDevicesList.size() == 0) { 2265 log("getDeviceForMessage: No connected device. what=" + what); 2266 return null; 2267 } 2268 for (BluetoothDevice device : mConnectedDevicesList) { 2269 if (getHandler().hasMessages(what, device)) { 2270 log("getDeviceForMessage: returning " + device); 2271 return device; 2272 } 2273 } 2274 log("getDeviceForMessage: No matching device for " + what + ". Returning null"); 2275 return null; 2276 } 2277 2278 private BluetoothDevice getMatchingDevice(BluetoothDevice device) { 2279 for (BluetoothDevice matchingDevice : mConnectedDevicesList) { 2280 if (matchingDevice.equals(device)) { 2281 return matchingDevice; 2282 } 2283 } 2284 return null; 2285 } 2286 2287 // This method does not check for error conditon (newState == prevState) 2288 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 2289 log("Connection state " + device + ": " + prevState + "->" + newState); 2290 if (prevState == BluetoothProfile.STATE_CONNECTED) { 2291 // Headset is disconnecting, stop Virtual call if active. 2292 terminateScoUsingVirtualVoiceCall(); 2293 } 2294 2295 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 2296 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 2297 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 2298 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2299 mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 2300 } 2301 2302 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 2303 if (prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 2304 // When SCO gets disconnected during call transfer, Virtual call 2305 // needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall. 2306 terminateScoUsingVirtualVoiceCall(); 2307 } 2308 Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 2309 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 2310 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 2311 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2312 mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 2313 log("Audio state " + device + ": " + prevState + "->" + newState); 2314 } 2315 2316 /* 2317 * Put the AT command, company ID, arguments, and device in an Intent and broadcast it. 2318 */ 2319 private void broadcastVendorSpecificEventIntent(String command, int companyId, int commandType, 2320 Object[] arguments, BluetoothDevice device) { 2321 log("broadcastVendorSpecificEventIntent(" + command + ")"); 2322 Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT); 2323 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command); 2324 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType); 2325 // assert: all elements of args are Serializable 2326 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments); 2327 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2328 2329 intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." 2330 + Integer.toString(companyId)); 2331 2332 mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 2333 } 2334 2335 private void configAudioParameters(BluetoothDevice device) { 2336 // Reset NREC on connect event. Headset will override later 2337 HashMap<String, Integer> AudioParamConfig = new HashMap<String, Integer>(); 2338 AudioParamConfig.put("NREC", 1); 2339 mHeadsetAudioParam.put(device, AudioParamConfig); 2340 mAudioManager.setParameters( 2341 HEADSET_NAME + "=" + getCurrentDeviceName(device) + ";" + HEADSET_NREC + "=on"); 2342 Log.d(TAG, "configAudioParameters for device:" + device + " are: nrec = " 2343 + AudioParamConfig.get("NREC")); 2344 } 2345 2346 private void setAudioParameters(BluetoothDevice device) { 2347 // 1. update nrec value 2348 // 2. update headset name 2349 int mNrec = 0; 2350 HashMap<String, Integer> AudioParam = mHeadsetAudioParam.get(device); 2351 if (AudioParam != null && !AudioParam.isEmpty()) { 2352 mNrec = AudioParam.get("NREC"); 2353 } else { 2354 Log.e(TAG, "setAudioParameters: AudioParam not found"); 2355 } 2356 2357 if (mNrec == 1) { 2358 Log.d(TAG, "Set NREC: 1 for device:" + device); 2359 mAudioManager.setParameters(HEADSET_NREC + "=on"); 2360 } else { 2361 Log.d(TAG, "Set NREC: 0 for device:" + device); 2362 mAudioManager.setParameters(HEADSET_NREC + "=off"); 2363 } 2364 mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device)); 2365 } 2366 2367 private String parseUnknownAt(String atString) { 2368 StringBuilder atCommand = new StringBuilder(atString.length()); 2369 String result = null; 2370 2371 for (int i = 0; i < atString.length(); i++) { 2372 char c = atString.charAt(i); 2373 if (c == '"') { 2374 int j = atString.indexOf('"', i + 1); // search for closing " 2375 if (j == -1) { // unmatched ", insert one. 2376 atCommand.append(atString.substring(i, atString.length())); 2377 atCommand.append('"'); 2378 break; 2379 } 2380 atCommand.append(atString.substring(i, j + 1)); 2381 i = j; 2382 } else if (c != ' ') { 2383 atCommand.append(Character.toUpperCase(c)); 2384 } 2385 } 2386 result = atCommand.toString(); 2387 return result; 2388 } 2389 2390 private int getAtCommandType(String atCommand) { 2391 int commandType = mPhonebook.TYPE_UNKNOWN; 2392 String atString = null; 2393 atCommand = atCommand.trim(); 2394 if (atCommand.length() > 5) { 2395 atString = atCommand.substring(5); 2396 if (atString.startsWith("?")) // Read 2397 commandType = mPhonebook.TYPE_READ; 2398 else if (atString.startsWith("=?")) // Test 2399 commandType = mPhonebook.TYPE_TEST; 2400 else if (atString.startsWith("=")) // Set 2401 commandType = mPhonebook.TYPE_SET; 2402 else 2403 commandType = mPhonebook.TYPE_UNKNOWN; 2404 } 2405 return commandType; 2406 } 2407 2408 /* Method to check if Virtual Call in Progress */ 2409 private boolean isVirtualCallInProgress() { 2410 return mVirtualCallStarted; 2411 } 2412 2413 void setVirtualCallInProgress(boolean state) { 2414 mVirtualCallStarted = state; 2415 } 2416 2417 /* NOTE: Currently the VirtualCall API does not support handling of 2418 call transfers. If it is initiated from the handsfree device, 2419 HeadsetStateMachine will end the virtual call by calling 2420 terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */ 2421 synchronized boolean initiateScoUsingVirtualVoiceCall() { 2422 if (DBG) log("initiateScoUsingVirtualVoiceCall: Received"); 2423 // 1. Check if the SCO state is idle 2424 if (isInCall() || mVoiceRecognitionStarted) { 2425 Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress."); 2426 return false; 2427 } 2428 2429 // 2. Send virtual phone state changed to initialize SCO 2430 processCallState( 2431 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true); 2432 processCallState( 2433 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true); 2434 processCallState( 2435 new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true); 2436 setVirtualCallInProgress(true); 2437 // Done 2438 if (DBG) log("initiateScoUsingVirtualVoiceCall: Done"); 2439 return true; 2440 } 2441 2442 synchronized boolean terminateScoUsingVirtualVoiceCall() { 2443 if (DBG) log("terminateScoUsingVirtualVoiceCall: Received"); 2444 2445 if (!isVirtualCallInProgress()) { 2446 Log.e(TAG, "terminateScoUsingVirtualVoiceCall:" 2447 + "No present call to terminate"); 2448 return false; 2449 } 2450 2451 // 2. Send virtual phone state changed to close SCO 2452 processCallState( 2453 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true); 2454 setVirtualCallInProgress(false); 2455 // Done 2456 if (DBG) log("terminateScoUsingVirtualVoiceCall: Done"); 2457 return true; 2458 } 2459 2460 private void processAnswerCall(BluetoothDevice device) { 2461 if (device == null) { 2462 Log.w(TAG, "processAnswerCall device is null"); 2463 return; 2464 } 2465 2466 if (mPhoneProxy != null) { 2467 try { 2468 mPhoneProxy.answerCall(); 2469 } catch (RemoteException e) { 2470 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2471 } 2472 } else { 2473 Log.e(TAG, "Handsfree phone proxy null for answering call"); 2474 } 2475 } 2476 2477 private void processHangupCall(BluetoothDevice device) { 2478 if (device == null) { 2479 Log.w(TAG, "processHangupCall device is null"); 2480 return; 2481 } 2482 // Close the virtual call if active. Virtual call should be 2483 // terminated for CHUP callback event 2484 if (isVirtualCallInProgress()) { 2485 terminateScoUsingVirtualVoiceCall(); 2486 } else { 2487 if (mPhoneProxy != null) { 2488 try { 2489 mPhoneProxy.hangupCall(); 2490 } catch (RemoteException e) { 2491 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2492 } 2493 } else { 2494 Log.e(TAG, "Handsfree phone proxy null for hanging up call"); 2495 } 2496 } 2497 } 2498 2499 private void processDialCall(String number, BluetoothDevice device) { 2500 if (device == null) { 2501 Log.w(TAG, "processDialCall device is null"); 2502 return; 2503 } 2504 2505 String dialNumber; 2506 if (mDialingOut) { 2507 if (DBG) log("processDialCall, already dialling"); 2508 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2509 return; 2510 } 2511 if ((number == null) || (number.length() == 0)) { 2512 dialNumber = mPhonebook.getLastDialledNumber(); 2513 if (dialNumber == null) { 2514 if (DBG) log("processDialCall, last dial number null"); 2515 atResponseCodeNative( 2516 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2517 return; 2518 } 2519 } else if (number.charAt(0) == '>') { 2520 // Yuck - memory dialling requested. 2521 // Just dial last number for now 2522 if (number.startsWith(">9999")) { // for PTS test 2523 atResponseCodeNative( 2524 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2525 return; 2526 } 2527 if (DBG) log("processDialCall, memory dial do last dial for now"); 2528 dialNumber = mPhonebook.getLastDialledNumber(); 2529 if (dialNumber == null) { 2530 if (DBG) log("processDialCall, last dial number null"); 2531 atResponseCodeNative( 2532 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2533 return; 2534 } 2535 } else { 2536 // Remove trailing ';' 2537 if (number.charAt(number.length() - 1) == ';') { 2538 number = number.substring(0, number.length() - 1); 2539 } 2540 2541 dialNumber = PhoneNumberUtils.convertPreDial(number); 2542 } 2543 // Check for virtual call to terminate before sending Call Intent 2544 terminateScoUsingVirtualVoiceCall(); 2545 2546 Intent intent = new Intent( 2547 Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null)); 2548 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2549 mService.startActivity(intent); 2550 // TODO(BT) continue send OK reults code after call starts 2551 // hold wait lock, start a timer, set wait call flag 2552 // Get call started indication from bluetooth phone 2553 mDialingOut = true; 2554 Message m = obtainMessage(DIALING_OUT_TIMEOUT); 2555 m.obj = getMatchingDevice(device); 2556 sendMessageDelayed(m, DIALING_OUT_TIMEOUT_VALUE); 2557 } 2558 2559 private void processVolumeEvent(int volumeType, int volume, BluetoothDevice device) { 2560 if (device != null && !device.equals(mActiveScoDevice) && mPhoneState.isInCall()) { 2561 Log.w(TAG, "ignore processVolumeEvent"); 2562 return; 2563 } 2564 2565 if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) { 2566 mPhoneState.setSpeakerVolume(volume); 2567 int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0; 2568 mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag); 2569 } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) { 2570 mPhoneState.setMicVolume(volume); 2571 } else { 2572 Log.e(TAG, "Bad voluem type: " + volumeType); 2573 } 2574 } 2575 2576 private void processSendDtmf(int dtmf, BluetoothDevice device) { 2577 if (device == null) { 2578 Log.w(TAG, "processSendDtmf device is null"); 2579 return; 2580 } 2581 2582 if (mPhoneProxy != null) { 2583 try { 2584 mPhoneProxy.sendDtmf(dtmf); 2585 } catch (RemoteException e) { 2586 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2587 } 2588 } else { 2589 Log.e(TAG, "Handsfree phone proxy null for sending DTMF"); 2590 } 2591 } 2592 2593 private void processCallState(HeadsetCallState callState) { 2594 processCallState(callState, false); 2595 } 2596 2597 private void processCallState(HeadsetCallState callState, boolean isVirtualCall) { 2598 mPhoneState.setNumActiveCall(callState.mNumActive); 2599 mPhoneState.setNumHeldCall(callState.mNumHeld); 2600 mPhoneState.setCallState(callState.mCallState); 2601 if (mDialingOut) { 2602 if (callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) { 2603 BluetoothDevice device = getDeviceForMessage(DIALING_OUT_TIMEOUT); 2604 if (device == null) { 2605 return; 2606 } 2607 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device)); 2608 removeMessages(DIALING_OUT_TIMEOUT); 2609 } else if (callState.mCallState == HeadsetHalConstants.CALL_STATE_ACTIVE 2610 || callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE) { 2611 mDialingOut = false; 2612 } 2613 } 2614 2615 /* Set ActiveScoDevice to null when call ends */ 2616 if ((mActiveScoDevice != null) && !isInCall() 2617 && callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE) 2618 mActiveScoDevice = null; 2619 2620 log("mNumActive: " + callState.mNumActive + " mNumHeld: " + callState.mNumHeld 2621 + " mCallState: " + callState.mCallState); 2622 log("mNumber: " + callState.mNumber + " mType: " + callState.mType); 2623 2624 if (isVirtualCall) { 2625 // virtual call state update 2626 if (getCurrentState() != mDisconnected) { 2627 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 2628 callState.mCallState, callState.mNumber, callState.mType); 2629 } 2630 } else { 2631 // circuit-switch voice call update 2632 // stop virtual voice call if there is a CSV call ongoing 2633 if (callState.mNumActive > 0 || callState.mNumHeld > 0 2634 || callState.mCallState != HeadsetHalConstants.CALL_STATE_IDLE) { 2635 terminateScoUsingVirtualVoiceCall(); 2636 } 2637 2638 // Specific handling for case of starting MO/MT call while VOIP 2639 // ongoing, terminateScoUsingVirtualVoiceCall() resets callState 2640 // INCOMING/DIALING to IDLE. Some HS send AT+CIND? to read call 2641 // and get wrong value of callsetup. This case is hit only 2642 // SCO for VOIP call is not terminated via SDK API call. 2643 if (mPhoneState.getCallState() != callState.mCallState) { 2644 mPhoneState.setCallState(callState.mCallState); 2645 } 2646 2647 // at this step: if there is virtual call ongoing, it means there is no CSV call 2648 // let virtual call continue and skip phone state update 2649 if (!isVirtualCallInProgress()) { 2650 if (getCurrentState() != mDisconnected) { 2651 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 2652 callState.mCallState, callState.mNumber, callState.mType); 2653 } 2654 } 2655 } 2656 } 2657 2658 // 1 enable noice reduction 2659 // 0 disable noice reduction 2660 private void processNoiceReductionEvent(int enable, BluetoothDevice device) { 2661 HashMap<String, Integer> AudioParamNrec = mHeadsetAudioParam.get(device); 2662 if (AudioParamNrec != null && !AudioParamNrec.isEmpty()) { 2663 if (enable == 1) 2664 AudioParamNrec.put("NREC", 1); 2665 else 2666 AudioParamNrec.put("NREC", 0); 2667 log("NREC value for device :" + device + " is: " + AudioParamNrec.get("NREC")); 2668 } else { 2669 Log.e(TAG, "processNoiceReductionEvent: AudioParamNrec is null "); 2670 } 2671 2672 if (mActiveScoDevice != null && mActiveScoDevice.equals(device) 2673 && mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 2674 setAudioParameters(device); 2675 } 2676 } 2677 2678 // 2 - WBS on 2679 // 1 - NBS on 2680 private void processWBSEvent(int enable, BluetoothDevice device) { 2681 if (enable == 2) { 2682 Log.d(TAG, "AudioManager.setParameters bt_wbs=on for " + device.getName() + " - " 2683 + device.getAddress()); 2684 mAudioManager.setParameters(HEADSET_WBS + "=on"); 2685 } else { 2686 Log.d(TAG, "AudioManager.setParameters bt_wbs=off for " + device.getName() + " - " 2687 + device.getAddress()); 2688 mAudioManager.setParameters(HEADSET_WBS + "=off"); 2689 } 2690 } 2691 2692 private void processAtChld(int chld, BluetoothDevice device) { 2693 if (device == null) { 2694 Log.w(TAG, "processAtChld device is null"); 2695 return; 2696 } 2697 2698 if (mPhoneProxy != null) { 2699 try { 2700 if (mPhoneProxy.processChld(chld)) { 2701 atResponseCodeNative( 2702 HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device)); 2703 } else { 2704 atResponseCodeNative( 2705 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2706 } 2707 } catch (RemoteException e) { 2708 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2709 atResponseCodeNative( 2710 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2711 } 2712 } else { 2713 Log.e(TAG, "Handsfree phone proxy null for At+Chld"); 2714 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2715 } 2716 } 2717 2718 private void processSubscriberNumberRequest(BluetoothDevice device) { 2719 if (device == null) { 2720 Log.w(TAG, "processSubscriberNumberRequest device is null"); 2721 return; 2722 } 2723 2724 if (mPhoneProxy != null) { 2725 try { 2726 String number = mPhoneProxy.getSubscriberNumber(); 2727 if (number != null) { 2728 atResponseStringNative("+CNUM: ,\"" + number + "\"," 2729 + PhoneNumberUtils.toaFromString(number) + ",,4", 2730 getByteAddress(device)); 2731 atResponseCodeNative( 2732 HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device)); 2733 } else { 2734 Log.e(TAG, "getSubscriberNumber returns null"); 2735 atResponseCodeNative( 2736 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2737 } 2738 } catch (RemoteException e) { 2739 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2740 atResponseCodeNative( 2741 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2742 } 2743 } else { 2744 Log.e(TAG, "Handsfree phone proxy null for At+CNUM"); 2745 } 2746 } 2747 2748 private void processAtCind(BluetoothDevice device) { 2749 int call, call_setup; 2750 2751 if (device == null) { 2752 Log.w(TAG, "processAtCind device is null"); 2753 return; 2754 } 2755 2756 /* Handsfree carkits expect that +CIND is properly responded to 2757 Hence we ensure that a proper response is sent 2758 for the virtual call too.*/ 2759 if (isVirtualCallInProgress()) { 2760 call = 1; 2761 call_setup = 0; 2762 } else { 2763 // regular phone call 2764 call = mPhoneState.getNumActiveCall(); 2765 call_setup = mPhoneState.getNumHeldCall(); 2766 } 2767 2768 cindResponseNative(mPhoneState.getService(), call, call_setup, mPhoneState.getCallState(), 2769 mPhoneState.getSignal(), mPhoneState.getRoam(), mPhoneState.getBatteryCharge(), 2770 getByteAddress(device)); 2771 } 2772 2773 private void processAtCops(BluetoothDevice device) { 2774 if (device == null) { 2775 Log.w(TAG, "processAtCops device is null"); 2776 return; 2777 } 2778 2779 if (mPhoneProxy != null) { 2780 try { 2781 String operatorName = mPhoneProxy.getNetworkOperator(); 2782 if (operatorName == null) { 2783 operatorName = ""; 2784 } 2785 copsResponseNative(operatorName, getByteAddress(device)); 2786 } catch (RemoteException e) { 2787 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2788 copsResponseNative("", getByteAddress(device)); 2789 } 2790 } else { 2791 Log.e(TAG, "Handsfree phone proxy null for At+COPS"); 2792 copsResponseNative("", getByteAddress(device)); 2793 } 2794 } 2795 2796 private void processAtClcc(BluetoothDevice device) { 2797 if (device == null) { 2798 Log.w(TAG, "processAtClcc device is null"); 2799 return; 2800 } 2801 2802 if (mPhoneProxy != null) { 2803 try { 2804 if (isVirtualCallInProgress()) { 2805 String phoneNumber = ""; 2806 int type = PhoneNumberUtils.TOA_Unknown; 2807 try { 2808 phoneNumber = mPhoneProxy.getSubscriberNumber(); 2809 type = PhoneNumberUtils.toaFromString(phoneNumber); 2810 } catch (RemoteException ee) { 2811 Log.e(TAG, "Unable to retrieve phone number" 2812 + "using IBluetoothHeadsetPhone proxy"); 2813 phoneNumber = ""; 2814 } 2815 clccResponseNative( 2816 1, 0, 0, 0, false, phoneNumber, type, getByteAddress(device)); 2817 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2818 } else if (!mPhoneProxy.listCurrentCalls()) { 2819 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2820 } else { 2821 Log.d(TAG, "Starting CLCC response timeout for device: " + device); 2822 Message m = obtainMessage(CLCC_RSP_TIMEOUT); 2823 m.obj = getMatchingDevice(device); 2824 sendMessageDelayed(m, CLCC_RSP_TIMEOUT_VALUE); 2825 } 2826 } catch (RemoteException e) { 2827 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2828 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2829 } 2830 } else { 2831 Log.e(TAG, "Handsfree phone proxy null for At+CLCC"); 2832 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2833 } 2834 } 2835 2836 private void processAtCscs(String atString, int type, BluetoothDevice device) { 2837 log("processAtCscs - atString = " + atString); 2838 if (mPhonebook != null) { 2839 mPhonebook.handleCscsCommand(atString, type, device); 2840 } else { 2841 Log.e(TAG, "Phonebook handle null for At+CSCS"); 2842 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2843 } 2844 } 2845 2846 private void processAtCpbs(String atString, int type, BluetoothDevice device) { 2847 log("processAtCpbs - atString = " + atString); 2848 if (mPhonebook != null) { 2849 mPhonebook.handleCpbsCommand(atString, type, device); 2850 } else { 2851 Log.e(TAG, "Phonebook handle null for At+CPBS"); 2852 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2853 } 2854 } 2855 2856 private void processAtCpbr(String atString, int type, BluetoothDevice device) { 2857 log("processAtCpbr - atString = " + atString); 2858 if (mPhonebook != null) { 2859 mPhonebook.handleCpbrCommand(atString, type, device); 2860 } else { 2861 Log.e(TAG, "Phonebook handle null for At+CPBR"); 2862 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2863 } 2864 } 2865 2866 /** 2867 * Find a character ch, ignoring quoted sections. 2868 * Return input.length() if not found. 2869 */ 2870 static private int findChar(char ch, String input, int fromIndex) { 2871 for (int i = fromIndex; i < input.length(); i++) { 2872 char c = input.charAt(i); 2873 if (c == '"') { 2874 i = input.indexOf('"', i + 1); 2875 if (i == -1) { 2876 return input.length(); 2877 } 2878 } else if (c == ch) { 2879 return i; 2880 } 2881 } 2882 return input.length(); 2883 } 2884 2885 /** 2886 * Break an argument string into individual arguments (comma delimited). 2887 * Integer arguments are turned into Integer objects. Otherwise a String 2888 * object is used. 2889 */ 2890 static private Object[] generateArgs(String input) { 2891 int i = 0; 2892 int j; 2893 ArrayList<Object> out = new ArrayList<Object>(); 2894 while (i <= input.length()) { 2895 j = findChar(',', input, i); 2896 2897 String arg = input.substring(i, j); 2898 try { 2899 out.add(new Integer(arg)); 2900 } catch (NumberFormatException e) { 2901 out.add(arg); 2902 } 2903 2904 i = j + 1; // move past comma 2905 } 2906 return out.toArray(); 2907 } 2908 2909 /** 2910 * @return {@code true} if the given string is a valid vendor-specific AT command. 2911 */ 2912 private boolean processVendorSpecificAt(String atString) { 2913 log("processVendorSpecificAt - atString = " + atString); 2914 2915 // Currently we accept only SET type commands. 2916 int indexOfEqual = atString.indexOf("="); 2917 if (indexOfEqual == -1) { 2918 Log.e(TAG, "processVendorSpecificAt: command type error in " + atString); 2919 return false; 2920 } 2921 2922 String command = atString.substring(0, indexOfEqual); 2923 Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command); 2924 if (companyId == null) { 2925 Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString); 2926 return false; 2927 } 2928 2929 String arg = atString.substring(indexOfEqual + 1); 2930 if (arg.startsWith("?")) { 2931 Log.e(TAG, "processVendorSpecificAt: command type error in " + atString); 2932 return false; 2933 } 2934 2935 Object[] args = generateArgs(arg); 2936 broadcastVendorSpecificEventIntent( 2937 command, companyId, BluetoothHeadset.AT_CMD_TYPE_SET, args, mCurrentDevice); 2938 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(mCurrentDevice)); 2939 return true; 2940 } 2941 2942 private void processUnknownAt(String atString, BluetoothDevice device) { 2943 if (device == null) { 2944 Log.w(TAG, "processUnknownAt device is null"); 2945 return; 2946 } 2947 2948 // TODO (BT) 2949 log("processUnknownAt - atString = " + atString); 2950 String atCommand = parseUnknownAt(atString); 2951 int commandType = getAtCommandType(atCommand); 2952 if (atCommand.startsWith("+CSCS")) 2953 processAtCscs(atCommand.substring(5), commandType, device); 2954 else if (atCommand.startsWith("+CPBS")) 2955 processAtCpbs(atCommand.substring(5), commandType, device); 2956 else if (atCommand.startsWith("+CPBR")) 2957 processAtCpbr(atCommand.substring(5), commandType, device); 2958 else if (!processVendorSpecificAt(atCommand)) 2959 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2960 } 2961 2962 private void processKeyPressed(BluetoothDevice device) { 2963 if (device == null) { 2964 Log.w(TAG, "processKeyPressed device is null"); 2965 return; 2966 } 2967 2968 if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) { 2969 if (mPhoneProxy != null) { 2970 try { 2971 mPhoneProxy.answerCall(); 2972 } catch (RemoteException e) { 2973 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2974 } 2975 } else { 2976 Log.e(TAG, "Handsfree phone proxy null for answering call"); 2977 } 2978 } else if (mPhoneState.getNumActiveCall() > 0) { 2979 if (!isAudioOn()) { 2980 connectAudioNative(getByteAddress(mCurrentDevice)); 2981 } else { 2982 if (mPhoneProxy != null) { 2983 try { 2984 mPhoneProxy.hangupCall(); 2985 } catch (RemoteException e) { 2986 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2987 } 2988 } else { 2989 Log.e(TAG, "Handsfree phone proxy null for hangup call"); 2990 } 2991 } 2992 } else { 2993 String dialNumber = mPhonebook.getLastDialledNumber(); 2994 if (dialNumber == null) { 2995 if (DBG) log("processKeyPressed, last dial number null"); 2996 return; 2997 } 2998 Intent intent = new Intent( 2999 Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null)); 3000 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3001 mService.startActivity(intent); 3002 } 3003 } 3004 3005 private void sendIndicatorIntent(BluetoothDevice device, int ind_id, String ind_value) { 3006 Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED); 3007 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 3008 intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, ind_id); 3009 if (ind_value != null) 3010 intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, ind_value); 3011 3012 mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 3013 } 3014 3015 private void processAtBind(String at_string, BluetoothDevice device) { 3016 log("processAtBind processAtBind: " + at_string); 3017 3018 // Parse the AT String to find the Indicator Ids that are supported 3019 int ind_id = 0; 3020 int iter = 0; 3021 int iter1 = 0; 3022 3023 while (iter < at_string.length()) { 3024 iter1 = findChar(',', at_string, iter); 3025 String id = at_string.substring(iter, iter1); 3026 3027 try { 3028 ind_id = new Integer(id); 3029 } catch (NumberFormatException e) { 3030 Log.e(TAG, Log.getStackTraceString(new Throwable())); 3031 } 3032 3033 switch (ind_id) { 3034 case HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY: 3035 log("Send Broadcast intent for the" 3036 + "Enhanced Driver Safety indicator."); 3037 sendIndicatorIntent(device, ind_id, null); 3038 break; 3039 case HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS: 3040 log("Send Broadcast intent for the" 3041 + "Battery Level indicator."); 3042 sendIndicatorIntent(device, ind_id, null); 3043 break; 3044 default: 3045 log("Invalid HF Indicator Received"); 3046 break; 3047 } 3048 3049 iter = iter1 + 1; // move past comma 3050 } 3051 } 3052 3053 private void processAtBiev(int ind_id, int ind_value, BluetoothDevice device) { 3054 log(" Process AT + BIEV Command : " + ind_id + ", " + ind_value); 3055 3056 String ind_value_str = Integer.toString(ind_value); 3057 3058 Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED); 3059 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 3060 sendIndicatorIntent(device, ind_id, ind_value_str); 3061 } 3062 3063 private void onConnectionStateChanged(int state, byte[] address) { 3064 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 3065 event.valueInt = state; 3066 event.device = getDevice(address); 3067 sendMessage(STACK_EVENT, event); 3068 } 3069 3070 private void onAudioStateChanged(int state, byte[] address) { 3071 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 3072 event.valueInt = state; 3073 event.device = getDevice(address); 3074 sendMessage(STACK_EVENT, event); 3075 } 3076 3077 private void onVrStateChanged(int state, byte[] address) { 3078 StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED); 3079 event.valueInt = state; 3080 event.device = getDevice(address); 3081 sendMessage(STACK_EVENT, event); 3082 } 3083 3084 private void onAnswerCall(byte[] address) { 3085 StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL); 3086 event.device = getDevice(address); 3087 sendMessage(STACK_EVENT, event); 3088 } 3089 3090 private void onHangupCall(byte[] address) { 3091 StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL); 3092 event.device = getDevice(address); 3093 sendMessage(STACK_EVENT, event); 3094 } 3095 3096 private void onVolumeChanged(int type, int volume, byte[] address) { 3097 StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED); 3098 event.valueInt = type; 3099 event.valueInt2 = volume; 3100 event.device = getDevice(address); 3101 sendMessage(STACK_EVENT, event); 3102 } 3103 3104 private void onDialCall(String number, byte[] address) { 3105 StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL); 3106 event.valueString = number; 3107 event.device = getDevice(address); 3108 sendMessage(STACK_EVENT, event); 3109 } 3110 3111 private void onSendDtmf(int dtmf, byte[] address) { 3112 StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF); 3113 event.valueInt = dtmf; 3114 event.device = getDevice(address); 3115 sendMessage(STACK_EVENT, event); 3116 } 3117 3118 private void onNoiceReductionEnable(boolean enable, byte[] address) { 3119 StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION); 3120 event.valueInt = enable ? 1 : 0; 3121 event.device = getDevice(address); 3122 sendMessage(STACK_EVENT, event); 3123 } 3124 3125 private void onWBS(int codec, byte[] address) { 3126 StackEvent event = new StackEvent(EVENT_TYPE_WBS); 3127 event.valueInt = codec; 3128 event.device = getDevice(address); 3129 sendMessage(STACK_EVENT, event); 3130 } 3131 3132 private void onAtChld(int chld, byte[] address) { 3133 StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD); 3134 event.valueInt = chld; 3135 event.device = getDevice(address); 3136 sendMessage(STACK_EVENT, event); 3137 } 3138 3139 private void onAtCnum(byte[] address) { 3140 StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST); 3141 event.device = getDevice(address); 3142 sendMessage(STACK_EVENT, event); 3143 } 3144 3145 private void onAtCind(byte[] address) { 3146 StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND); 3147 event.device = getDevice(address); 3148 sendMessage(STACK_EVENT, event); 3149 } 3150 3151 private void onAtCops(byte[] address) { 3152 StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS); 3153 event.device = getDevice(address); 3154 sendMessage(STACK_EVENT, event); 3155 } 3156 3157 private void onAtClcc(byte[] address) { 3158 StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC); 3159 event.device = getDevice(address); 3160 sendMessage(STACK_EVENT, event); 3161 } 3162 3163 private void onUnknownAt(String atString, byte[] address) { 3164 StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT); 3165 event.valueString = atString; 3166 event.device = getDevice(address); 3167 sendMessage(STACK_EVENT, event); 3168 } 3169 3170 private void onKeyPressed(byte[] address) { 3171 StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED); 3172 event.device = getDevice(address); 3173 sendMessage(STACK_EVENT, event); 3174 } 3175 3176 private void onATBind(String atString, byte[] address) { 3177 StackEvent event = new StackEvent(EVENT_TYPE_BIND); 3178 event.valueString = atString; 3179 event.device = getDevice(address); 3180 sendMessage(STACK_EVENT, event); 3181 } 3182 3183 private void onATBiev(int ind_id, int ind_value, byte[] address) { 3184 StackEvent event = new StackEvent(EVENT_TYPE_BIEV); 3185 event.valueInt = ind_id; 3186 event.valueInt2 = ind_value; 3187 event.device = getDevice(address); 3188 sendMessage(STACK_EVENT, event); 3189 } 3190 3191 private void processIntentBatteryChanged(Intent intent) { 3192 int batteryLevel = intent.getIntExtra("level", -1); 3193 int scale = intent.getIntExtra("scale", -1); 3194 if (batteryLevel == -1 || scale == -1 || scale == 0) { 3195 Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale); 3196 return; 3197 } 3198 batteryLevel = batteryLevel * 5 / scale; 3199 mPhoneState.setBatteryCharge(batteryLevel); 3200 } 3201 3202 private void processDeviceStateChanged(HeadsetDeviceState deviceState) { 3203 notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal, 3204 deviceState.mBatteryCharge); 3205 } 3206 3207 private void processSendClccResponse(HeadsetClccResponse clcc) { 3208 BluetoothDevice device = getDeviceForMessage(CLCC_RSP_TIMEOUT); 3209 if (device == null) { 3210 return; 3211 } 3212 if (clcc.mIndex == 0) { 3213 removeMessages(CLCC_RSP_TIMEOUT); 3214 } 3215 clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty, 3216 clcc.mNumber, clcc.mType, getByteAddress(device)); 3217 } 3218 3219 private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) { 3220 String stringToSend = resultCode.mCommand + ": "; 3221 if (resultCode.mArg != null) { 3222 stringToSend += resultCode.mArg; 3223 } 3224 atResponseStringNative(stringToSend, getByteAddress(resultCode.mDevice)); 3225 } 3226 3227 private String getCurrentDeviceName(BluetoothDevice device) { 3228 String defaultName = "<unknown>"; 3229 3230 if (device == null) { 3231 return defaultName; 3232 } 3233 3234 String deviceName = device.getName(); 3235 if (deviceName == null) { 3236 return defaultName; 3237 } 3238 return deviceName; 3239 } 3240 3241 private byte[] getByteAddress(BluetoothDevice device) { 3242 return Utils.getBytesFromAddress(device.getAddress()); 3243 } 3244 3245 private BluetoothDevice getDevice(byte[] address) { 3246 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 3247 } 3248 3249 private boolean isInCall() { 3250 return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) 3251 || ((mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE) 3252 && (mPhoneState.getCallState() 3253 != HeadsetHalConstants.CALL_STATE_INCOMING))); 3254 } 3255 3256 // Accept incoming SCO only when there is active call, VR activated, 3257 // active VOIP call 3258 private boolean isScoAcceptable() { 3259 return mAudioRouteAllowed && (mVoiceRecognitionStarted || isInCall()); 3260 } 3261 3262 boolean isConnected() { 3263 IState currentState = getCurrentState(); 3264 return (currentState == mConnected || currentState == mAudioOn); 3265 } 3266 3267 boolean okToConnect(BluetoothDevice device) { 3268 AdapterService adapterService = AdapterService.getAdapterService(); 3269 int priority = mService.getPriority(device); 3270 boolean ret = false; 3271 // check if this is an incoming connection in Quiet mode. 3272 if ((adapterService == null) 3273 || ((adapterService.isQuietModeEnabled() == true) && (mTargetDevice == null))) { 3274 ret = false; 3275 } 3276 // check priority and accept or reject the connection. if priority is undefined 3277 // it is likely that our SDP has not completed and peer is initiating the 3278 // connection. Allow this connection, provided the device is bonded 3279 else if ((BluetoothProfile.PRIORITY_OFF < priority) 3280 || ((BluetoothProfile.PRIORITY_UNDEFINED == priority) 3281 && (device.getBondState() != BluetoothDevice.BOND_NONE))) { 3282 ret = true; 3283 } 3284 return ret; 3285 } 3286 3287 @Override 3288 protected void log(String msg) { 3289 if (DBG) { 3290 super.log(msg); 3291 } 3292 } 3293 3294 public void handleAccessPermissionResult(Intent intent) { 3295 log("handleAccessPermissionResult"); 3296 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 3297 if (mPhonebook != null) { 3298 if (!mPhonebook.getCheckingAccessPermission()) { 3299 return; 3300 } 3301 int atCommandResult = 0; 3302 int atCommandErrorCode = 0; 3303 // HeadsetBase headset = mHandsfree.getHeadset(); 3304 // ASSERT: (headset != null) && headSet.isConnected() 3305 // REASON: mCheckingAccessPermission is true, otherwise resetAtState 3306 // has set mCheckingAccessPermission to false 3307 if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 3308 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 3309 BluetoothDevice.CONNECTION_ACCESS_NO) 3310 == BluetoothDevice.CONNECTION_ACCESS_YES) { 3311 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 3312 mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); 3313 } 3314 atCommandResult = mPhonebook.processCpbrCommand(device); 3315 } else { 3316 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 3317 mCurrentDevice.setPhonebookAccessPermission( 3318 BluetoothDevice.ACCESS_REJECTED); 3319 } 3320 } 3321 } 3322 mPhonebook.setCpbrIndex(-1); 3323 mPhonebook.setCheckingAccessPermission(false); 3324 3325 if (atCommandResult >= 0) { 3326 atResponseCodeNative(atCommandResult, atCommandErrorCode, getByteAddress(device)); 3327 } else { 3328 log("handleAccessPermissionResult - RESULT_NONE"); 3329 } 3330 } else { 3331 Log.e(TAG, "Phonebook handle null"); 3332 if (device != null) { 3333 atResponseCodeNative( 3334 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 3335 } 3336 } 3337 } 3338 3339 private static final String SCHEME_TEL = "tel"; 3340 3341 // Event types for STACK_EVENT message 3342 final private static int EVENT_TYPE_NONE = 0; 3343 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 3344 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 3345 final private static int EVENT_TYPE_VR_STATE_CHANGED = 3; 3346 final private static int EVENT_TYPE_ANSWER_CALL = 4; 3347 final private static int EVENT_TYPE_HANGUP_CALL = 5; 3348 final private static int EVENT_TYPE_VOLUME_CHANGED = 6; 3349 final private static int EVENT_TYPE_DIAL_CALL = 7; 3350 final private static int EVENT_TYPE_SEND_DTMF = 8; 3351 final private static int EVENT_TYPE_NOICE_REDUCTION = 9; 3352 final private static int EVENT_TYPE_AT_CHLD = 10; 3353 final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11; 3354 final private static int EVENT_TYPE_AT_CIND = 12; 3355 final private static int EVENT_TYPE_AT_COPS = 13; 3356 final private static int EVENT_TYPE_AT_CLCC = 14; 3357 final private static int EVENT_TYPE_UNKNOWN_AT = 15; 3358 final private static int EVENT_TYPE_KEY_PRESSED = 16; 3359 final private static int EVENT_TYPE_WBS = 17; 3360 final private static int EVENT_TYPE_BIND = 18; 3361 final private static int EVENT_TYPE_BIEV = 19; 3362 3363 private class StackEvent { 3364 int type = EVENT_TYPE_NONE; 3365 int valueInt = 0; 3366 int valueInt2 = 0; 3367 String valueString = null; 3368 BluetoothDevice device = null; 3369 3370 private StackEvent(int type) { 3371 this.type = type; 3372 } 3373 } 3374 3375 /*package*/ native boolean atResponseCodeNative( 3376 int responseCode, int errorCode, byte[] address); 3377 /*package*/ native boolean atResponseStringNative(String responseString, byte[] address); 3378 3379 private native static void classInitNative(); 3380 private native void initializeNative(int max_hf_clients); 3381 private native void cleanupNative(); 3382 private native boolean connectHfpNative(byte[] address); 3383 private native boolean disconnectHfpNative(byte[] address); 3384 private native boolean connectAudioNative(byte[] address); 3385 private native boolean disconnectAudioNative(byte[] address); 3386 private native boolean startVoiceRecognitionNative(byte[] address); 3387 private native boolean stopVoiceRecognitionNative(byte[] address); 3388 private native boolean setVolumeNative(int volumeType, int volume, byte[] address); 3389 private native boolean cindResponseNative(int service, int numActive, int numHeld, 3390 int callState, int signal, int roam, int batteryCharge, byte[] address); 3391 private native boolean bindResponseNative(int ind_id, boolean ind_status, byte[] address); 3392 private native boolean notifyDeviceStatusNative( 3393 int networkState, int serviceType, int signal, int batteryCharge); 3394 3395 private native boolean clccResponseNative(int index, int dir, int status, int mode, 3396 boolean mpty, String number, int type, byte[] address); 3397 private native boolean copsResponseNative(String operatorName, byte[] address); 3398 3399 private native boolean phoneStateChangeNative( 3400 int numActive, int numHeld, int callState, String number, int type); 3401 private native boolean configureWBSNative(byte[] address, int condec_config); 3402} 3403