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