HeadsetStateMachine.java revision 3659dee2b21c7f269a2bef051483093fe07e5682
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 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 2300 mService.sendBroadcastAsUser(intent, UserHandle.ALL, 2301 HeadsetService.BLUETOOTH_PERM); 2302 } 2303 2304 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 2305 if (prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 2306 // When SCO gets disconnected during call transfer, Virtual call 2307 // needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall. 2308 terminateScoUsingVirtualVoiceCall(); 2309 } 2310 Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 2311 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 2312 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 2313 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2314 mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 2315 log("Audio state " + device + ": " + prevState + "->" + newState); 2316 } 2317 2318 /* 2319 * Put the AT command, company ID, arguments, and device in an Intent and broadcast it. 2320 */ 2321 private void broadcastVendorSpecificEventIntent(String command, int companyId, int commandType, 2322 Object[] arguments, BluetoothDevice device) { 2323 log("broadcastVendorSpecificEventIntent(" + command + ")"); 2324 Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT); 2325 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command); 2326 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType); 2327 // assert: all elements of args are Serializable 2328 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments); 2329 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2330 2331 intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." 2332 + Integer.toString(companyId)); 2333 2334 mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 2335 } 2336 2337 private void configAudioParameters(BluetoothDevice device) { 2338 // Reset NREC on connect event. Headset will override later 2339 HashMap<String, Integer> AudioParamConfig = new HashMap<String, Integer>(); 2340 AudioParamConfig.put("NREC", 1); 2341 mHeadsetAudioParam.put(device, AudioParamConfig); 2342 mAudioManager.setParameters( 2343 HEADSET_NAME + "=" + getCurrentDeviceName(device) + ";" + HEADSET_NREC + "=on"); 2344 Log.d(TAG, "configAudioParameters for device:" + device + " are: nrec = " 2345 + AudioParamConfig.get("NREC")); 2346 } 2347 2348 private void setAudioParameters(BluetoothDevice device) { 2349 // 1. update nrec value 2350 // 2. update headset name 2351 int mNrec = 0; 2352 HashMap<String, Integer> AudioParam = mHeadsetAudioParam.get(device); 2353 if (AudioParam != null && !AudioParam.isEmpty()) { 2354 mNrec = AudioParam.get("NREC"); 2355 } else { 2356 Log.e(TAG, "setAudioParameters: AudioParam not found"); 2357 } 2358 2359 if (mNrec == 1) { 2360 Log.d(TAG, "Set NREC: 1 for device:" + device); 2361 mAudioManager.setParameters(HEADSET_NREC + "=on"); 2362 } else { 2363 Log.d(TAG, "Set NREC: 0 for device:" + device); 2364 mAudioManager.setParameters(HEADSET_NREC + "=off"); 2365 } 2366 mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device)); 2367 } 2368 2369 private String parseUnknownAt(String atString) { 2370 StringBuilder atCommand = new StringBuilder(atString.length()); 2371 String result = null; 2372 2373 for (int i = 0; i < atString.length(); i++) { 2374 char c = atString.charAt(i); 2375 if (c == '"') { 2376 int j = atString.indexOf('"', i + 1); // search for closing " 2377 if (j == -1) { // unmatched ", insert one. 2378 atCommand.append(atString.substring(i, atString.length())); 2379 atCommand.append('"'); 2380 break; 2381 } 2382 atCommand.append(atString.substring(i, j + 1)); 2383 i = j; 2384 } else if (c != ' ') { 2385 atCommand.append(Character.toUpperCase(c)); 2386 } 2387 } 2388 result = atCommand.toString(); 2389 return result; 2390 } 2391 2392 private int getAtCommandType(String atCommand) { 2393 int commandType = mPhonebook.TYPE_UNKNOWN; 2394 String atString = null; 2395 atCommand = atCommand.trim(); 2396 if (atCommand.length() > 5) { 2397 atString = atCommand.substring(5); 2398 if (atString.startsWith("?")) // Read 2399 commandType = mPhonebook.TYPE_READ; 2400 else if (atString.startsWith("=?")) // Test 2401 commandType = mPhonebook.TYPE_TEST; 2402 else if (atString.startsWith("=")) // Set 2403 commandType = mPhonebook.TYPE_SET; 2404 else 2405 commandType = mPhonebook.TYPE_UNKNOWN; 2406 } 2407 return commandType; 2408 } 2409 2410 /* Method to check if Virtual Call in Progress */ 2411 private boolean isVirtualCallInProgress() { 2412 return mVirtualCallStarted; 2413 } 2414 2415 void setVirtualCallInProgress(boolean state) { 2416 mVirtualCallStarted = state; 2417 } 2418 2419 /* NOTE: Currently the VirtualCall API does not support handling of 2420 call transfers. If it is initiated from the handsfree device, 2421 HeadsetStateMachine will end the virtual call by calling 2422 terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */ 2423 synchronized boolean initiateScoUsingVirtualVoiceCall() { 2424 if (DBG) log("initiateScoUsingVirtualVoiceCall: Received"); 2425 // 1. Check if the SCO state is idle 2426 if (isInCall() || mVoiceRecognitionStarted) { 2427 Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress."); 2428 return false; 2429 } 2430 2431 // 2. Send virtual phone state changed to initialize SCO 2432 processCallState( 2433 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true); 2434 processCallState( 2435 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true); 2436 processCallState( 2437 new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true); 2438 setVirtualCallInProgress(true); 2439 // Done 2440 if (DBG) log("initiateScoUsingVirtualVoiceCall: Done"); 2441 return true; 2442 } 2443 2444 synchronized boolean terminateScoUsingVirtualVoiceCall() { 2445 if (DBG) log("terminateScoUsingVirtualVoiceCall: Received"); 2446 2447 if (!isVirtualCallInProgress()) { 2448 Log.e(TAG, "terminateScoUsingVirtualVoiceCall:" 2449 + "No present call to terminate"); 2450 return false; 2451 } 2452 2453 // 2. Send virtual phone state changed to close SCO 2454 processCallState( 2455 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true); 2456 setVirtualCallInProgress(false); 2457 // Done 2458 if (DBG) log("terminateScoUsingVirtualVoiceCall: Done"); 2459 return true; 2460 } 2461 2462 private void processAnswerCall(BluetoothDevice device) { 2463 if (device == null) { 2464 Log.w(TAG, "processAnswerCall device is null"); 2465 return; 2466 } 2467 2468 if (mPhoneProxy != null) { 2469 try { 2470 mPhoneProxy.answerCall(); 2471 } catch (RemoteException e) { 2472 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2473 } 2474 } else { 2475 Log.e(TAG, "Handsfree phone proxy null for answering call"); 2476 } 2477 } 2478 2479 private void processHangupCall(BluetoothDevice device) { 2480 if (device == null) { 2481 Log.w(TAG, "processHangupCall device is null"); 2482 return; 2483 } 2484 // Close the virtual call if active. Virtual call should be 2485 // terminated for CHUP callback event 2486 if (isVirtualCallInProgress()) { 2487 terminateScoUsingVirtualVoiceCall(); 2488 } else { 2489 if (mPhoneProxy != null) { 2490 try { 2491 mPhoneProxy.hangupCall(); 2492 } catch (RemoteException e) { 2493 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2494 } 2495 } else { 2496 Log.e(TAG, "Handsfree phone proxy null for hanging up call"); 2497 } 2498 } 2499 } 2500 2501 private void processDialCall(String number, BluetoothDevice device) { 2502 if (device == null) { 2503 Log.w(TAG, "processDialCall device is null"); 2504 return; 2505 } 2506 2507 String dialNumber; 2508 if (mDialingOut) { 2509 if (DBG) log("processDialCall, already dialling"); 2510 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2511 return; 2512 } 2513 if ((number == null) || (number.length() == 0)) { 2514 dialNumber = mPhonebook.getLastDialledNumber(); 2515 if (dialNumber == null) { 2516 if (DBG) log("processDialCall, last dial number null"); 2517 atResponseCodeNative( 2518 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2519 return; 2520 } 2521 } else if (number.charAt(0) == '>') { 2522 // Yuck - memory dialling requested. 2523 // Just dial last number for now 2524 if (number.startsWith(">9999")) { // for PTS test 2525 atResponseCodeNative( 2526 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2527 return; 2528 } 2529 if (DBG) log("processDialCall, memory dial do last dial for now"); 2530 dialNumber = mPhonebook.getLastDialledNumber(); 2531 if (dialNumber == null) { 2532 if (DBG) log("processDialCall, last dial number null"); 2533 atResponseCodeNative( 2534 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2535 return; 2536 } 2537 } else { 2538 // Remove trailing ';' 2539 if (number.charAt(number.length() - 1) == ';') { 2540 number = number.substring(0, number.length() - 1); 2541 } 2542 2543 dialNumber = PhoneNumberUtils.convertPreDial(number); 2544 } 2545 // Check for virtual call to terminate before sending Call Intent 2546 terminateScoUsingVirtualVoiceCall(); 2547 2548 Intent intent = new Intent( 2549 Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null)); 2550 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2551 mService.startActivity(intent); 2552 // TODO(BT) continue send OK reults code after call starts 2553 // hold wait lock, start a timer, set wait call flag 2554 // Get call started indication from bluetooth phone 2555 mDialingOut = true; 2556 Message m = obtainMessage(DIALING_OUT_TIMEOUT); 2557 m.obj = getMatchingDevice(device); 2558 sendMessageDelayed(m, DIALING_OUT_TIMEOUT_VALUE); 2559 } 2560 2561 private void processVolumeEvent(int volumeType, int volume, BluetoothDevice device) { 2562 if (device != null && !device.equals(mActiveScoDevice) && mPhoneState.isInCall()) { 2563 Log.w(TAG, "ignore processVolumeEvent"); 2564 return; 2565 } 2566 2567 if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) { 2568 mPhoneState.setSpeakerVolume(volume); 2569 int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0; 2570 mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag); 2571 } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) { 2572 mPhoneState.setMicVolume(volume); 2573 } else { 2574 Log.e(TAG, "Bad voluem type: " + volumeType); 2575 } 2576 } 2577 2578 private void processSendDtmf(int dtmf, BluetoothDevice device) { 2579 if (device == null) { 2580 Log.w(TAG, "processSendDtmf device is null"); 2581 return; 2582 } 2583 2584 if (mPhoneProxy != null) { 2585 try { 2586 mPhoneProxy.sendDtmf(dtmf); 2587 } catch (RemoteException e) { 2588 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2589 } 2590 } else { 2591 Log.e(TAG, "Handsfree phone proxy null for sending DTMF"); 2592 } 2593 } 2594 2595 private void processCallState(HeadsetCallState callState) { 2596 processCallState(callState, false); 2597 } 2598 2599 private void processCallState(HeadsetCallState callState, boolean isVirtualCall) { 2600 mPhoneState.setNumActiveCall(callState.mNumActive); 2601 mPhoneState.setNumHeldCall(callState.mNumHeld); 2602 mPhoneState.setCallState(callState.mCallState); 2603 if (mDialingOut) { 2604 if (callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) { 2605 BluetoothDevice device = getDeviceForMessage(DIALING_OUT_TIMEOUT); 2606 if (device == null) { 2607 return; 2608 } 2609 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device)); 2610 removeMessages(DIALING_OUT_TIMEOUT); 2611 } else if (callState.mCallState == HeadsetHalConstants.CALL_STATE_ACTIVE 2612 || callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE) { 2613 mDialingOut = false; 2614 } 2615 } 2616 2617 /* Set ActiveScoDevice to null when call ends */ 2618 if ((mActiveScoDevice != null) && !isInCall() 2619 && callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE) 2620 mActiveScoDevice = null; 2621 2622 log("mNumActive: " + callState.mNumActive + " mNumHeld: " + callState.mNumHeld 2623 + " mCallState: " + callState.mCallState); 2624 log("mNumber: " + callState.mNumber + " mType: " + callState.mType); 2625 2626 if (isVirtualCall) { 2627 // virtual call state update 2628 if (getCurrentState() != mDisconnected) { 2629 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 2630 callState.mCallState, callState.mNumber, callState.mType); 2631 } 2632 } else { 2633 // circuit-switch voice call update 2634 // stop virtual voice call if there is a CSV call ongoing 2635 if (callState.mNumActive > 0 || callState.mNumHeld > 0 2636 || callState.mCallState != HeadsetHalConstants.CALL_STATE_IDLE) { 2637 terminateScoUsingVirtualVoiceCall(); 2638 } 2639 2640 // Specific handling for case of starting MO/MT call while VOIP 2641 // ongoing, terminateScoUsingVirtualVoiceCall() resets callState 2642 // INCOMING/DIALING to IDLE. Some HS send AT+CIND? to read call 2643 // and get wrong value of callsetup. This case is hit only 2644 // SCO for VOIP call is not terminated via SDK API call. 2645 if (mPhoneState.getCallState() != callState.mCallState) { 2646 mPhoneState.setCallState(callState.mCallState); 2647 } 2648 2649 // at this step: if there is virtual call ongoing, it means there is no CSV call 2650 // let virtual call continue and skip phone state update 2651 if (!isVirtualCallInProgress()) { 2652 if (getCurrentState() != mDisconnected) { 2653 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 2654 callState.mCallState, callState.mNumber, callState.mType); 2655 } 2656 } 2657 } 2658 } 2659 2660 // 1 enable noice reduction 2661 // 0 disable noice reduction 2662 private void processNoiceReductionEvent(int enable, BluetoothDevice device) { 2663 HashMap<String, Integer> AudioParamNrec = mHeadsetAudioParam.get(device); 2664 if (AudioParamNrec != null && !AudioParamNrec.isEmpty()) { 2665 if (enable == 1) 2666 AudioParamNrec.put("NREC", 1); 2667 else 2668 AudioParamNrec.put("NREC", 0); 2669 log("NREC value for device :" + device + " is: " + AudioParamNrec.get("NREC")); 2670 } else { 2671 Log.e(TAG, "processNoiceReductionEvent: AudioParamNrec is null "); 2672 } 2673 2674 if (mActiveScoDevice != null && mActiveScoDevice.equals(device) 2675 && mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 2676 setAudioParameters(device); 2677 } 2678 } 2679 2680 // 2 - WBS on 2681 // 1 - NBS on 2682 private void processWBSEvent(int enable, BluetoothDevice device) { 2683 if (enable == 2) { 2684 Log.d(TAG, "AudioManager.setParameters bt_wbs=on for " + device.getName() + " - " 2685 + device.getAddress()); 2686 mAudioManager.setParameters(HEADSET_WBS + "=on"); 2687 } else { 2688 Log.d(TAG, "AudioManager.setParameters bt_wbs=off for " + device.getName() + " - " 2689 + device.getAddress()); 2690 mAudioManager.setParameters(HEADSET_WBS + "=off"); 2691 } 2692 } 2693 2694 private void processAtChld(int chld, BluetoothDevice device) { 2695 if (device == null) { 2696 Log.w(TAG, "processAtChld device is null"); 2697 return; 2698 } 2699 2700 if (mPhoneProxy != null) { 2701 try { 2702 if (mPhoneProxy.processChld(chld)) { 2703 atResponseCodeNative( 2704 HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device)); 2705 } else { 2706 atResponseCodeNative( 2707 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2708 } 2709 } catch (RemoteException e) { 2710 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2711 atResponseCodeNative( 2712 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2713 } 2714 } else { 2715 Log.e(TAG, "Handsfree phone proxy null for At+Chld"); 2716 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2717 } 2718 } 2719 2720 private void processSubscriberNumberRequest(BluetoothDevice device) { 2721 if (device == null) { 2722 Log.w(TAG, "processSubscriberNumberRequest device is null"); 2723 return; 2724 } 2725 2726 if (mPhoneProxy != null) { 2727 try { 2728 String number = mPhoneProxy.getSubscriberNumber(); 2729 if (number != null) { 2730 atResponseStringNative("+CNUM: ,\"" + number + "\"," 2731 + PhoneNumberUtils.toaFromString(number) + ",,4", 2732 getByteAddress(device)); 2733 atResponseCodeNative( 2734 HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device)); 2735 } else { 2736 Log.e(TAG, "getSubscriberNumber returns null"); 2737 atResponseCodeNative( 2738 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2739 } 2740 } catch (RemoteException e) { 2741 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2742 atResponseCodeNative( 2743 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2744 } 2745 } else { 2746 Log.e(TAG, "Handsfree phone proxy null for At+CNUM"); 2747 } 2748 } 2749 2750 private void processAtCind(BluetoothDevice device) { 2751 int call, call_setup; 2752 2753 if (device == null) { 2754 Log.w(TAG, "processAtCind device is null"); 2755 return; 2756 } 2757 2758 /* Handsfree carkits expect that +CIND is properly responded to 2759 Hence we ensure that a proper response is sent 2760 for the virtual call too.*/ 2761 if (isVirtualCallInProgress()) { 2762 call = 1; 2763 call_setup = 0; 2764 } else { 2765 // regular phone call 2766 call = mPhoneState.getNumActiveCall(); 2767 call_setup = mPhoneState.getNumHeldCall(); 2768 } 2769 2770 cindResponseNative(mPhoneState.getService(), call, call_setup, mPhoneState.getCallState(), 2771 mPhoneState.getSignal(), mPhoneState.getRoam(), mPhoneState.getBatteryCharge(), 2772 getByteAddress(device)); 2773 } 2774 2775 private void processAtCops(BluetoothDevice device) { 2776 if (device == null) { 2777 Log.w(TAG, "processAtCops device is null"); 2778 return; 2779 } 2780 2781 if (mPhoneProxy != null) { 2782 try { 2783 String operatorName = mPhoneProxy.getNetworkOperator(); 2784 if (operatorName == null) { 2785 operatorName = ""; 2786 } 2787 copsResponseNative(operatorName, getByteAddress(device)); 2788 } catch (RemoteException e) { 2789 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2790 copsResponseNative("", getByteAddress(device)); 2791 } 2792 } else { 2793 Log.e(TAG, "Handsfree phone proxy null for At+COPS"); 2794 copsResponseNative("", getByteAddress(device)); 2795 } 2796 } 2797 2798 private void processAtClcc(BluetoothDevice device) { 2799 if (device == null) { 2800 Log.w(TAG, "processAtClcc device is null"); 2801 return; 2802 } 2803 2804 if (mPhoneProxy != null) { 2805 try { 2806 if (isVirtualCallInProgress()) { 2807 String phoneNumber = ""; 2808 int type = PhoneNumberUtils.TOA_Unknown; 2809 try { 2810 phoneNumber = mPhoneProxy.getSubscriberNumber(); 2811 type = PhoneNumberUtils.toaFromString(phoneNumber); 2812 } catch (RemoteException ee) { 2813 Log.e(TAG, "Unable to retrieve phone number" 2814 + "using IBluetoothHeadsetPhone proxy"); 2815 phoneNumber = ""; 2816 } 2817 clccResponseNative( 2818 1, 0, 0, 0, false, phoneNumber, type, getByteAddress(device)); 2819 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2820 } else if (!mPhoneProxy.listCurrentCalls()) { 2821 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2822 } else { 2823 Log.d(TAG, "Starting CLCC response timeout for device: " + device); 2824 Message m = obtainMessage(CLCC_RSP_TIMEOUT); 2825 m.obj = getMatchingDevice(device); 2826 sendMessageDelayed(m, CLCC_RSP_TIMEOUT_VALUE); 2827 } 2828 } catch (RemoteException e) { 2829 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2830 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2831 } 2832 } else { 2833 Log.e(TAG, "Handsfree phone proxy null for At+CLCC"); 2834 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2835 } 2836 } 2837 2838 private void processAtCscs(String atString, int type, BluetoothDevice device) { 2839 log("processAtCscs - atString = " + atString); 2840 if (mPhonebook != null) { 2841 mPhonebook.handleCscsCommand(atString, type, device); 2842 } else { 2843 Log.e(TAG, "Phonebook handle null for At+CSCS"); 2844 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2845 } 2846 } 2847 2848 private void processAtCpbs(String atString, int type, BluetoothDevice device) { 2849 log("processAtCpbs - atString = " + atString); 2850 if (mPhonebook != null) { 2851 mPhonebook.handleCpbsCommand(atString, type, device); 2852 } else { 2853 Log.e(TAG, "Phonebook handle null for At+CPBS"); 2854 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2855 } 2856 } 2857 2858 private void processAtCpbr(String atString, int type, BluetoothDevice device) { 2859 log("processAtCpbr - atString = " + atString); 2860 if (mPhonebook != null) { 2861 mPhonebook.handleCpbrCommand(atString, type, device); 2862 } else { 2863 Log.e(TAG, "Phonebook handle null for At+CPBR"); 2864 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2865 } 2866 } 2867 2868 /** 2869 * Find a character ch, ignoring quoted sections. 2870 * Return input.length() if not found. 2871 */ 2872 static private int findChar(char ch, String input, int fromIndex) { 2873 for (int i = fromIndex; i < input.length(); i++) { 2874 char c = input.charAt(i); 2875 if (c == '"') { 2876 i = input.indexOf('"', i + 1); 2877 if (i == -1) { 2878 return input.length(); 2879 } 2880 } else if (c == ch) { 2881 return i; 2882 } 2883 } 2884 return input.length(); 2885 } 2886 2887 /** 2888 * Break an argument string into individual arguments (comma delimited). 2889 * Integer arguments are turned into Integer objects. Otherwise a String 2890 * object is used. 2891 */ 2892 static private Object[] generateArgs(String input) { 2893 int i = 0; 2894 int j; 2895 ArrayList<Object> out = new ArrayList<Object>(); 2896 while (i <= input.length()) { 2897 j = findChar(',', input, i); 2898 2899 String arg = input.substring(i, j); 2900 try { 2901 out.add(new Integer(arg)); 2902 } catch (NumberFormatException e) { 2903 out.add(arg); 2904 } 2905 2906 i = j + 1; // move past comma 2907 } 2908 return out.toArray(); 2909 } 2910 2911 /** 2912 * @return {@code true} if the given string is a valid vendor-specific AT command. 2913 */ 2914 private boolean processVendorSpecificAt(String atString) { 2915 log("processVendorSpecificAt - atString = " + atString); 2916 2917 // Currently we accept only SET type commands. 2918 int indexOfEqual = atString.indexOf("="); 2919 if (indexOfEqual == -1) { 2920 Log.e(TAG, "processVendorSpecificAt: command type error in " + atString); 2921 return false; 2922 } 2923 2924 String command = atString.substring(0, indexOfEqual); 2925 Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command); 2926 if (companyId == null) { 2927 Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString); 2928 return false; 2929 } 2930 2931 String arg = atString.substring(indexOfEqual + 1); 2932 if (arg.startsWith("?")) { 2933 Log.e(TAG, "processVendorSpecificAt: command type error in " + atString); 2934 return false; 2935 } 2936 2937 Object[] args = generateArgs(arg); 2938 broadcastVendorSpecificEventIntent( 2939 command, companyId, BluetoothHeadset.AT_CMD_TYPE_SET, args, mCurrentDevice); 2940 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(mCurrentDevice)); 2941 return true; 2942 } 2943 2944 private void processUnknownAt(String atString, BluetoothDevice device) { 2945 if (device == null) { 2946 Log.w(TAG, "processUnknownAt device is null"); 2947 return; 2948 } 2949 2950 // TODO (BT) 2951 log("processUnknownAt - atString = " + atString); 2952 String atCommand = parseUnknownAt(atString); 2953 int commandType = getAtCommandType(atCommand); 2954 if (atCommand.startsWith("+CSCS")) 2955 processAtCscs(atCommand.substring(5), commandType, device); 2956 else if (atCommand.startsWith("+CPBS")) 2957 processAtCpbs(atCommand.substring(5), commandType, device); 2958 else if (atCommand.startsWith("+CPBR")) 2959 processAtCpbr(atCommand.substring(5), commandType, device); 2960 else if (!processVendorSpecificAt(atCommand)) 2961 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2962 } 2963 2964 private void processKeyPressed(BluetoothDevice device) { 2965 if (device == null) { 2966 Log.w(TAG, "processKeyPressed device is null"); 2967 return; 2968 } 2969 2970 if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) { 2971 if (mPhoneProxy != null) { 2972 try { 2973 mPhoneProxy.answerCall(); 2974 } catch (RemoteException e) { 2975 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2976 } 2977 } else { 2978 Log.e(TAG, "Handsfree phone proxy null for answering call"); 2979 } 2980 } else if (mPhoneState.getNumActiveCall() > 0) { 2981 if (!isAudioOn()) { 2982 connectAudioNative(getByteAddress(mCurrentDevice)); 2983 } else { 2984 if (mPhoneProxy != null) { 2985 try { 2986 mPhoneProxy.hangupCall(); 2987 } catch (RemoteException e) { 2988 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2989 } 2990 } else { 2991 Log.e(TAG, "Handsfree phone proxy null for hangup call"); 2992 } 2993 } 2994 } else { 2995 String dialNumber = mPhonebook.getLastDialledNumber(); 2996 if (dialNumber == null) { 2997 if (DBG) log("processKeyPressed, last dial number null"); 2998 return; 2999 } 3000 Intent intent = new Intent( 3001 Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null)); 3002 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3003 mService.startActivity(intent); 3004 } 3005 } 3006 3007 private void sendIndicatorIntent(BluetoothDevice device, int ind_id, String ind_value) { 3008 Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED); 3009 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 3010 intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, ind_id); 3011 if (ind_value != null) 3012 intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, ind_value); 3013 3014 mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 3015 } 3016 3017 private void processAtBind(String at_string, BluetoothDevice device) { 3018 log("processAtBind processAtBind: " + at_string); 3019 3020 // Parse the AT String to find the Indicator Ids that are supported 3021 int ind_id = 0; 3022 int iter = 0; 3023 int iter1 = 0; 3024 3025 while (iter < at_string.length()) { 3026 iter1 = findChar(',', at_string, iter); 3027 String id = at_string.substring(iter, iter1); 3028 3029 try { 3030 ind_id = new Integer(id); 3031 } catch (NumberFormatException e) { 3032 Log.e(TAG, Log.getStackTraceString(new Throwable())); 3033 } 3034 3035 switch (ind_id) { 3036 case HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY: 3037 log("Send Broadcast intent for the" 3038 + "Enhanced Driver Safety indicator."); 3039 sendIndicatorIntent(device, ind_id, null); 3040 break; 3041 case HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS: 3042 log("Send Broadcast intent for the" 3043 + "Battery Level indicator."); 3044 sendIndicatorIntent(device, ind_id, null); 3045 break; 3046 default: 3047 log("Invalid HF Indicator Received"); 3048 break; 3049 } 3050 3051 iter = iter1 + 1; // move past comma 3052 } 3053 } 3054 3055 private void processAtBiev(int ind_id, int ind_value, BluetoothDevice device) { 3056 log(" Process AT + BIEV Command : " + ind_id + ", " + ind_value); 3057 3058 String ind_value_str = Integer.toString(ind_value); 3059 3060 Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED); 3061 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 3062 sendIndicatorIntent(device, ind_id, ind_value_str); 3063 } 3064 3065 private void onConnectionStateChanged(int state, byte[] address) { 3066 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 3067 event.valueInt = state; 3068 event.device = getDevice(address); 3069 sendMessage(STACK_EVENT, event); 3070 } 3071 3072 private void onAudioStateChanged(int state, byte[] address) { 3073 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 3074 event.valueInt = state; 3075 event.device = getDevice(address); 3076 sendMessage(STACK_EVENT, event); 3077 } 3078 3079 private void onVrStateChanged(int state, byte[] address) { 3080 StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED); 3081 event.valueInt = state; 3082 event.device = getDevice(address); 3083 sendMessage(STACK_EVENT, event); 3084 } 3085 3086 private void onAnswerCall(byte[] address) { 3087 StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL); 3088 event.device = getDevice(address); 3089 sendMessage(STACK_EVENT, event); 3090 } 3091 3092 private void onHangupCall(byte[] address) { 3093 StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL); 3094 event.device = getDevice(address); 3095 sendMessage(STACK_EVENT, event); 3096 } 3097 3098 private void onVolumeChanged(int type, int volume, byte[] address) { 3099 StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED); 3100 event.valueInt = type; 3101 event.valueInt2 = volume; 3102 event.device = getDevice(address); 3103 sendMessage(STACK_EVENT, event); 3104 } 3105 3106 private void onDialCall(String number, byte[] address) { 3107 StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL); 3108 event.valueString = number; 3109 event.device = getDevice(address); 3110 sendMessage(STACK_EVENT, event); 3111 } 3112 3113 private void onSendDtmf(int dtmf, byte[] address) { 3114 StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF); 3115 event.valueInt = dtmf; 3116 event.device = getDevice(address); 3117 sendMessage(STACK_EVENT, event); 3118 } 3119 3120 private void onNoiceReductionEnable(boolean enable, byte[] address) { 3121 StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION); 3122 event.valueInt = enable ? 1 : 0; 3123 event.device = getDevice(address); 3124 sendMessage(STACK_EVENT, event); 3125 } 3126 3127 private void onWBS(int codec, byte[] address) { 3128 StackEvent event = new StackEvent(EVENT_TYPE_WBS); 3129 event.valueInt = codec; 3130 event.device = getDevice(address); 3131 sendMessage(STACK_EVENT, event); 3132 } 3133 3134 private void onAtChld(int chld, byte[] address) { 3135 StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD); 3136 event.valueInt = chld; 3137 event.device = getDevice(address); 3138 sendMessage(STACK_EVENT, event); 3139 } 3140 3141 private void onAtCnum(byte[] address) { 3142 StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST); 3143 event.device = getDevice(address); 3144 sendMessage(STACK_EVENT, event); 3145 } 3146 3147 private void onAtCind(byte[] address) { 3148 StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND); 3149 event.device = getDevice(address); 3150 sendMessage(STACK_EVENT, event); 3151 } 3152 3153 private void onAtCops(byte[] address) { 3154 StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS); 3155 event.device = getDevice(address); 3156 sendMessage(STACK_EVENT, event); 3157 } 3158 3159 private void onAtClcc(byte[] address) { 3160 StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC); 3161 event.device = getDevice(address); 3162 sendMessage(STACK_EVENT, event); 3163 } 3164 3165 private void onUnknownAt(String atString, byte[] address) { 3166 StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT); 3167 event.valueString = atString; 3168 event.device = getDevice(address); 3169 sendMessage(STACK_EVENT, event); 3170 } 3171 3172 private void onKeyPressed(byte[] address) { 3173 StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED); 3174 event.device = getDevice(address); 3175 sendMessage(STACK_EVENT, event); 3176 } 3177 3178 private void onATBind(String atString, byte[] address) { 3179 StackEvent event = new StackEvent(EVENT_TYPE_BIND); 3180 event.valueString = atString; 3181 event.device = getDevice(address); 3182 sendMessage(STACK_EVENT, event); 3183 } 3184 3185 private void onATBiev(int ind_id, int ind_value, byte[] address) { 3186 StackEvent event = new StackEvent(EVENT_TYPE_BIEV); 3187 event.valueInt = ind_id; 3188 event.valueInt2 = ind_value; 3189 event.device = getDevice(address); 3190 sendMessage(STACK_EVENT, event); 3191 } 3192 3193 private void processIntentBatteryChanged(Intent intent) { 3194 int batteryLevel = intent.getIntExtra("level", -1); 3195 int scale = intent.getIntExtra("scale", -1); 3196 if (batteryLevel == -1 || scale == -1 || scale == 0) { 3197 Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale); 3198 return; 3199 } 3200 batteryLevel = batteryLevel * 5 / scale; 3201 mPhoneState.setBatteryCharge(batteryLevel); 3202 } 3203 3204 private void processDeviceStateChanged(HeadsetDeviceState deviceState) { 3205 notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal, 3206 deviceState.mBatteryCharge); 3207 } 3208 3209 private void processSendClccResponse(HeadsetClccResponse clcc) { 3210 BluetoothDevice device = getDeviceForMessage(CLCC_RSP_TIMEOUT); 3211 if (device == null) { 3212 return; 3213 } 3214 if (clcc.mIndex == 0) { 3215 removeMessages(CLCC_RSP_TIMEOUT); 3216 } 3217 clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty, 3218 clcc.mNumber, clcc.mType, getByteAddress(device)); 3219 } 3220 3221 private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) { 3222 String stringToSend = resultCode.mCommand + ": "; 3223 if (resultCode.mArg != null) { 3224 stringToSend += resultCode.mArg; 3225 } 3226 atResponseStringNative(stringToSend, getByteAddress(resultCode.mDevice)); 3227 } 3228 3229 private String getCurrentDeviceName(BluetoothDevice device) { 3230 String defaultName = "<unknown>"; 3231 3232 if (device == null) { 3233 return defaultName; 3234 } 3235 3236 String deviceName = device.getName(); 3237 if (deviceName == null) { 3238 return defaultName; 3239 } 3240 return deviceName; 3241 } 3242 3243 private byte[] getByteAddress(BluetoothDevice device) { 3244 return Utils.getBytesFromAddress(device.getAddress()); 3245 } 3246 3247 private BluetoothDevice getDevice(byte[] address) { 3248 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 3249 } 3250 3251 private boolean isInCall() { 3252 return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) 3253 || ((mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE) 3254 && (mPhoneState.getCallState() 3255 != HeadsetHalConstants.CALL_STATE_INCOMING))); 3256 } 3257 3258 // Accept incoming SCO only when there is active call, VR activated, 3259 // active VOIP call 3260 private boolean isScoAcceptable() { 3261 return mAudioRouteAllowed && (mVoiceRecognitionStarted || isInCall()); 3262 } 3263 3264 boolean isConnected() { 3265 IState currentState = getCurrentState(); 3266 return (currentState == mConnected || currentState == mAudioOn); 3267 } 3268 3269 boolean okToConnect(BluetoothDevice device) { 3270 AdapterService adapterService = AdapterService.getAdapterService(); 3271 int priority = mService.getPriority(device); 3272 boolean ret = false; 3273 // check if this is an incoming connection in Quiet mode. 3274 if ((adapterService == null) 3275 || ((adapterService.isQuietModeEnabled() == true) && (mTargetDevice == null))) { 3276 ret = false; 3277 } 3278 // check priority and accept or reject the connection. if priority is undefined 3279 // it is likely that our SDP has not completed and peer is initiating the 3280 // connection. Allow this connection, provided the device is bonded 3281 else if ((BluetoothProfile.PRIORITY_OFF < priority) 3282 || ((BluetoothProfile.PRIORITY_UNDEFINED == priority) 3283 && (device.getBondState() != BluetoothDevice.BOND_NONE))) { 3284 ret = true; 3285 } 3286 return ret; 3287 } 3288 3289 @Override 3290 protected void log(String msg) { 3291 if (DBG) { 3292 super.log(msg); 3293 } 3294 } 3295 3296 public void handleAccessPermissionResult(Intent intent) { 3297 log("handleAccessPermissionResult"); 3298 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 3299 if (mPhonebook != null) { 3300 if (!mPhonebook.getCheckingAccessPermission()) { 3301 return; 3302 } 3303 int atCommandResult = 0; 3304 int atCommandErrorCode = 0; 3305 // HeadsetBase headset = mHandsfree.getHeadset(); 3306 // ASSERT: (headset != null) && headSet.isConnected() 3307 // REASON: mCheckingAccessPermission is true, otherwise resetAtState 3308 // has set mCheckingAccessPermission to false 3309 if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 3310 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 3311 BluetoothDevice.CONNECTION_ACCESS_NO) 3312 == BluetoothDevice.CONNECTION_ACCESS_YES) { 3313 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 3314 mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); 3315 } 3316 atCommandResult = mPhonebook.processCpbrCommand(device); 3317 } else { 3318 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 3319 mCurrentDevice.setPhonebookAccessPermission( 3320 BluetoothDevice.ACCESS_REJECTED); 3321 } 3322 } 3323 } 3324 mPhonebook.setCpbrIndex(-1); 3325 mPhonebook.setCheckingAccessPermission(false); 3326 3327 if (atCommandResult >= 0) { 3328 atResponseCodeNative(atCommandResult, atCommandErrorCode, getByteAddress(device)); 3329 } else { 3330 log("handleAccessPermissionResult - RESULT_NONE"); 3331 } 3332 } else { 3333 Log.e(TAG, "Phonebook handle null"); 3334 if (device != null) { 3335 atResponseCodeNative( 3336 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 3337 } 3338 } 3339 } 3340 3341 private static final String SCHEME_TEL = "tel"; 3342 3343 // Event types for STACK_EVENT message 3344 final private static int EVENT_TYPE_NONE = 0; 3345 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 3346 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 3347 final private static int EVENT_TYPE_VR_STATE_CHANGED = 3; 3348 final private static int EVENT_TYPE_ANSWER_CALL = 4; 3349 final private static int EVENT_TYPE_HANGUP_CALL = 5; 3350 final private static int EVENT_TYPE_VOLUME_CHANGED = 6; 3351 final private static int EVENT_TYPE_DIAL_CALL = 7; 3352 final private static int EVENT_TYPE_SEND_DTMF = 8; 3353 final private static int EVENT_TYPE_NOICE_REDUCTION = 9; 3354 final private static int EVENT_TYPE_AT_CHLD = 10; 3355 final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11; 3356 final private static int EVENT_TYPE_AT_CIND = 12; 3357 final private static int EVENT_TYPE_AT_COPS = 13; 3358 final private static int EVENT_TYPE_AT_CLCC = 14; 3359 final private static int EVENT_TYPE_UNKNOWN_AT = 15; 3360 final private static int EVENT_TYPE_KEY_PRESSED = 16; 3361 final private static int EVENT_TYPE_WBS = 17; 3362 final private static int EVENT_TYPE_BIND = 18; 3363 final private static int EVENT_TYPE_BIEV = 19; 3364 3365 private class StackEvent { 3366 int type = EVENT_TYPE_NONE; 3367 int valueInt = 0; 3368 int valueInt2 = 0; 3369 String valueString = null; 3370 BluetoothDevice device = null; 3371 3372 private StackEvent(int type) { 3373 this.type = type; 3374 } 3375 } 3376 3377 /*package*/ native boolean atResponseCodeNative( 3378 int responseCode, int errorCode, byte[] address); 3379 /*package*/ native boolean atResponseStringNative(String responseString, byte[] address); 3380 3381 private native static void classInitNative(); 3382 private native void initializeNative(int max_hf_clients); 3383 private native void cleanupNative(); 3384 private native boolean connectHfpNative(byte[] address); 3385 private native boolean disconnectHfpNative(byte[] address); 3386 private native boolean connectAudioNative(byte[] address); 3387 private native boolean disconnectAudioNative(byte[] address); 3388 private native boolean startVoiceRecognitionNative(byte[] address); 3389 private native boolean stopVoiceRecognitionNative(byte[] address); 3390 private native boolean setVolumeNative(int volumeType, int volume, byte[] address); 3391 private native boolean cindResponseNative(int service, int numActive, int numHeld, 3392 int callState, int signal, int roam, int batteryCharge, byte[] address); 3393 private native boolean bindResponseNative(int ind_id, boolean ind_status, byte[] address); 3394 private native boolean notifyDeviceStatusNative( 3395 int networkState, int serviceType, int signal, int batteryCharge); 3396 3397 private native boolean clccResponseNative(int index, int dir, int status, int mode, 3398 boolean mpty, String number, int type, byte[] address); 3399 private native boolean copsResponseNative(String operatorName, byte[] address); 3400 3401 private native boolean phoneStateChangeNative( 3402 int numActive, int numHeld, int callState, String number, int type); 3403 private native boolean configureWBSNative(byte[] address, int condec_config); 3404} 3405