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