HeadsetStateMachine.java revision 8e79653c4d093d6f6696312f668429feb6d62bf3
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 processIntentScoVolume((Intent) message.obj, mActiveScoDevice); 1306 break; 1307 case CALL_STATE_CHANGED: 1308 processCallState((HeadsetCallState) message.obj, ((message.arg1 == 1)?true:false)); 1309 break; 1310 case INTENT_BATTERY_CHANGED: 1311 processIntentBatteryChanged((Intent) message.obj); 1312 break; 1313 case DEVICE_STATE_CHANGED: 1314 processDeviceStateChanged((HeadsetDeviceState) message.obj); 1315 break; 1316 case SEND_CCLC_RESPONSE: 1317 processSendClccResponse((HeadsetClccResponse) message.obj); 1318 break; 1319 case CLCC_RSP_TIMEOUT: 1320 { 1321 BluetoothDevice device = (BluetoothDevice) message.obj; 1322 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 1323 } 1324 break; 1325 case SEND_VENDOR_SPECIFIC_RESULT_CODE: 1326 processSendVendorSpecificResultCode( 1327 (HeadsetVendorSpecificResultCode) message.obj); 1328 break; 1329 1330 case VIRTUAL_CALL_START: 1331 initiateScoUsingVirtualVoiceCall(); 1332 break; 1333 case VIRTUAL_CALL_STOP: 1334 terminateScoUsingVirtualVoiceCall(); 1335 break; 1336 1337 case DIALING_OUT_TIMEOUT: 1338 { 1339 if (mDialingOut) { 1340 BluetoothDevice device = (BluetoothDevice)message.obj; 1341 mDialingOut= false; 1342 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 1343 0, getByteAddress(device)); 1344 } 1345 } 1346 break; 1347 case START_VR_TIMEOUT: 1348 { 1349 if (mWaitingForVoiceRecognition) { 1350 BluetoothDevice device = (BluetoothDevice)message.obj; 1351 mWaitingForVoiceRecognition = false; 1352 Log.e(TAG, "Timeout waiting for voice recognition" + 1353 "to start"); 1354 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 1355 0, getByteAddress(device)); 1356 } 1357 } 1358 break; 1359 case STACK_EVENT: 1360 StackEvent event = (StackEvent) message.obj; 1361 if (DBG) { 1362 log("event type: " + event.type); 1363 } 1364 switch (event.type) { 1365 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 1366 BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT); 1367 if (device1 != null && device1.equals(event.device)) { 1368 Log.d(TAG, "remove connect timeout for device = " + device1); 1369 removeMessages(CONNECT_TIMEOUT); 1370 } 1371 processConnectionEvent(event.valueInt, event.device); 1372 break; 1373 case EVENT_TYPE_AUDIO_STATE_CHANGED: 1374 processAudioEvent(event.valueInt, event.device); 1375 break; 1376 case EVENT_TYPE_VR_STATE_CHANGED: 1377 processVrEvent(event.valueInt, event.device); 1378 break; 1379 case EVENT_TYPE_ANSWER_CALL: 1380 processAnswerCall(event.device); 1381 break; 1382 case EVENT_TYPE_HANGUP_CALL: 1383 processHangupCall(event.device); 1384 break; 1385 case EVENT_TYPE_VOLUME_CHANGED: 1386 processVolumeEvent(event.valueInt, event.valueInt2, 1387 event.device); 1388 break; 1389 case EVENT_TYPE_DIAL_CALL: 1390 processDialCall(event.valueString, event.device); 1391 break; 1392 case EVENT_TYPE_SEND_DTMF: 1393 processSendDtmf(event.valueInt, event.device); 1394 break; 1395 case EVENT_TYPE_NOICE_REDUCTION: 1396 processNoiceReductionEvent(event.valueInt, event.device); 1397 break; 1398 case EVENT_TYPE_AT_CHLD: 1399 processAtChld(event.valueInt, event.device); 1400 break; 1401 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 1402 processSubscriberNumberRequest(event.device); 1403 break; 1404 case EVENT_TYPE_AT_CIND: 1405 processAtCind(event.device); 1406 break; 1407 case EVENT_TYPE_AT_COPS: 1408 processAtCops(event.device); 1409 break; 1410 case EVENT_TYPE_AT_CLCC: 1411 processAtClcc(event.device); 1412 break; 1413 case EVENT_TYPE_UNKNOWN_AT: 1414 processUnknownAt(event.valueString, event.device); 1415 break; 1416 case EVENT_TYPE_KEY_PRESSED: 1417 processKeyPressed(event.device); 1418 break; 1419 default: 1420 Log.e(TAG, "Unknown stack event: " + event.type); 1421 break; 1422 } 1423 break; 1424 default: 1425 return NOT_HANDLED; 1426 } 1427 return retValue; 1428 } 1429 1430 // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this 1431 private void processConnectionEvent(int state, BluetoothDevice device) { 1432 Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + 1433 device); 1434 switch (state) { 1435 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 1436 if (mConnectedDevicesList.contains(device)) { 1437 if (mActiveScoDevice != null 1438 && mActiveScoDevice.equals(device)&& mAudioState 1439 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1440 processAudioEvent( 1441 HeadsetHalConstants.AUDIO_STATE_DISCONNECTED, device); 1442 } 1443 1444 synchronized (HeadsetStateMachine.this) { 1445 mConnectedDevicesList.remove(device); 1446 mHeadsetAudioParam.remove(device); 1447 mHeadsetBrsf.remove(device); 1448 Log.d(TAG, "device " + device.getAddress() + 1449 " is removed in AudioOn state"); 1450 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1451 BluetoothProfile.STATE_CONNECTED); 1452 processWBSEvent(0, device); /* disable WBS audio parameters */ 1453 if (mConnectedDevicesList.size() == 0) { 1454 transitionTo(mDisconnected); 1455 } 1456 else { 1457 processMultiHFConnected(device); 1458 } 1459 } 1460 } else { 1461 Log.e(TAG, "Disconnected from unknown device: " + device); 1462 } 1463 break; 1464 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 1465 processSlcConnected(); 1466 break; 1467 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 1468 if (mConnectedDevicesList.contains(device)) { 1469 mIncomingDevice = null; 1470 mTargetDevice = null; 1471 break; 1472 } 1473 Log.w(TAG, "HFP to be Connected in AudioOn state"); 1474 if (okToConnect(device) && (mConnectedDevicesList.size() 1475 < max_hf_connections) ) { 1476 Log.i(TAG,"Incoming Hf accepted"); 1477 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1478 BluetoothProfile.STATE_DISCONNECTED); 1479 synchronized (HeadsetStateMachine.this) { 1480 if (!mConnectedDevicesList.contains(device)) { 1481 mCurrentDevice = device; 1482 mConnectedDevicesList.add(device); 1483 Log.d(TAG, "device " + device.getAddress() + 1484 " is added in AudioOn state"); 1485 } 1486 } 1487 configAudioParameters(device); 1488 } else { 1489 // reject the connection and stay in Connected state itself 1490 Log.i(TAG,"Incoming Hf rejected. priority=" 1491 + mService.getPriority(device) + 1492 " bondState=" + device.getBondState()); 1493 disconnectHfpNative(getByteAddress(device)); 1494 // the other profile connection should be initiated 1495 AdapterService adapterService = AdapterService.getAdapterService(); 1496 if (adapterService != null) { 1497 adapterService.connectOtherProfile(device, 1498 AdapterService.PROFILE_CONN_REJECTED); 1499 } 1500 } 1501 break; 1502 default: 1503 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1504 break; 1505 } 1506 } 1507 1508 // in AudioOn state 1509 private void processAudioEvent(int state, BluetoothDevice device) { 1510 if (!mConnectedDevicesList.contains(device)) { 1511 Log.e(TAG, "Audio changed on disconnected device: " + device); 1512 return; 1513 } 1514 1515 switch (state) { 1516 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1517 if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1518 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 1519 mAudioManager.setBluetoothScoOn(false); 1520 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 1521 BluetoothHeadset.STATE_AUDIO_CONNECTED); 1522 } 1523 transitionTo(mConnected); 1524 break; 1525 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1526 // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset? 1527 //broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING, 1528 // BluetoothHeadset.STATE_AUDIO_CONNECTED); 1529 break; 1530 default: 1531 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1532 break; 1533 } 1534 } 1535 1536 private void processSlcConnected() { 1537 if (mPhoneProxy != null) { 1538 try { 1539 mPhoneProxy.queryPhoneState(); 1540 } catch (RemoteException e) { 1541 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1542 } 1543 } else { 1544 Log.e(TAG, "Handsfree phone proxy null for query phone state"); 1545 } 1546 } 1547 1548 private void processIntentScoVolume(Intent intent, BluetoothDevice device) { 1549 int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 1550 if (mPhoneState.getSpeakerVolume() != volumeValue) { 1551 mPhoneState.setSpeakerVolume(volumeValue); 1552 setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK, 1553 volumeValue, getByteAddress(device)); 1554 } 1555 } 1556 1557 private void processMultiHFConnected(BluetoothDevice device) { 1558 log("AudioOn state: processMultiHFConnected"); 1559 /* Assign the current activedevice again if the disconnected 1560 device equals to the current active device */ 1561 if (mCurrentDevice != null && mCurrentDevice.equals(device)) { 1562 int deviceSize = mConnectedDevicesList.size(); 1563 mCurrentDevice = mConnectedDevicesList.get(deviceSize-1); 1564 } 1565 if (mAudioState != BluetoothHeadset.STATE_AUDIO_CONNECTED) 1566 transitionTo(mConnected); 1567 1568 log("processMultiHFConnected , the latest mCurrentDevice is:" 1569 + mCurrentDevice); 1570 log("AudioOn state: processMultiHFConnected ," + 1571 "fake broadcasting for mCurrentDevice"); 1572 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 1573 BluetoothProfile.STATE_DISCONNECTED); 1574 } 1575 } 1576 1577 /* Add MultiHFPending state when atleast 1 HS is connected 1578 and disconnect/connect new HS */ 1579 private class MultiHFPending extends State { 1580 @Override 1581 public void enter() { 1582 log("Enter MultiHFPending: " + getCurrentMessage().what + 1583 ", size: " + mConnectedDevicesList.size()); 1584 } 1585 1586 @Override 1587 public boolean processMessage(Message message) { 1588 log("MultiHFPending process message: " + message.what + 1589 ", size: " + mConnectedDevicesList.size()); 1590 1591 boolean retValue = HANDLED; 1592 switch(message.what) { 1593 case CONNECT: 1594 deferMessage(message); 1595 break; 1596 1597 case CONNECT_AUDIO: 1598 if (mCurrentDevice != null) { 1599 connectAudioNative(getByteAddress(mCurrentDevice)); 1600 } 1601 break; 1602 case CONNECT_TIMEOUT: 1603 onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, 1604 getByteAddress(mTargetDevice)); 1605 break; 1606 1607 case DISCONNECT_AUDIO: 1608 if (mActiveScoDevice != null) { 1609 if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) { 1610 Log.d(TAG, "MultiHFPending, Disconnecting SCO audio for " + 1611 mActiveScoDevice); 1612 } else { 1613 Log.e(TAG, "disconnectAudioNative failed" + 1614 "for device = " + mActiveScoDevice); 1615 } 1616 } 1617 break; 1618 case DISCONNECT: 1619 BluetoothDevice device = (BluetoothDevice) message.obj; 1620 if (mConnectedDevicesList.contains(device) && 1621 mTargetDevice != null && mTargetDevice.equals(device)) { 1622 // cancel connection to the mTargetDevice 1623 broadcastConnectionState(device, 1624 BluetoothProfile.STATE_DISCONNECTED, 1625 BluetoothProfile.STATE_CONNECTING); 1626 synchronized (HeadsetStateMachine.this) { 1627 mTargetDevice = null; 1628 } 1629 } else { 1630 deferMessage(message); 1631 } 1632 break; 1633 case VOICE_RECOGNITION_START: 1634 device = (BluetoothDevice) message.obj; 1635 if (mConnectedDevicesList.contains(device)) { 1636 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED); 1637 } 1638 break; 1639 case VOICE_RECOGNITION_STOP: 1640 device = (BluetoothDevice) message.obj; 1641 if (mConnectedDevicesList.contains(device)) { 1642 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); 1643 } 1644 break; 1645 case INTENT_BATTERY_CHANGED: 1646 processIntentBatteryChanged((Intent) message.obj); 1647 break; 1648 case CALL_STATE_CHANGED: 1649 processCallState((HeadsetCallState) message.obj, 1650 ((message.arg1 == 1)?true:false)); 1651 break; 1652 case DEVICE_STATE_CHANGED: 1653 processDeviceStateChanged((HeadsetDeviceState) message.obj); 1654 break; 1655 case SEND_CCLC_RESPONSE: 1656 processSendClccResponse((HeadsetClccResponse) message.obj); 1657 break; 1658 case CLCC_RSP_TIMEOUT: 1659 { 1660 device = (BluetoothDevice) message.obj; 1661 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 1662 } 1663 break; 1664 case DIALING_OUT_TIMEOUT: 1665 if (mDialingOut) { 1666 device = (BluetoothDevice) message.obj; 1667 mDialingOut= false; 1668 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 1669 0, getByteAddress(device)); 1670 } 1671 break; 1672 case VIRTUAL_CALL_START: 1673 device = (BluetoothDevice) message.obj; 1674 if(mConnectedDevicesList.contains(device)) { 1675 initiateScoUsingVirtualVoiceCall(); 1676 } 1677 break; 1678 case VIRTUAL_CALL_STOP: 1679 device = (BluetoothDevice) message.obj; 1680 if (mConnectedDevicesList.contains(device)) { 1681 terminateScoUsingVirtualVoiceCall(); 1682 } 1683 break; 1684 case START_VR_TIMEOUT: 1685 if (mWaitingForVoiceRecognition) { 1686 device = (BluetoothDevice) message.obj; 1687 mWaitingForVoiceRecognition = false; 1688 Log.e(TAG, "Timeout waiting for voice" + 1689 "recognition to start"); 1690 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 1691 0, getByteAddress(device)); 1692 } 1693 break; 1694 case STACK_EVENT: 1695 StackEvent event = (StackEvent) message.obj; 1696 if (DBG) { 1697 log("event type: " + event.type); 1698 } 1699 switch (event.type) { 1700 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 1701 BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT); 1702 if (device1 != null && device1.equals(event.device)) { 1703 Log.d(TAG, "remove connect timeout for device = " + device1); 1704 removeMessages(CONNECT_TIMEOUT); 1705 } 1706 processConnectionEvent(event.valueInt, event.device); 1707 break; 1708 case EVENT_TYPE_AUDIO_STATE_CHANGED: 1709 processAudioEvent(event.valueInt, event.device); 1710 break; 1711 case EVENT_TYPE_VR_STATE_CHANGED: 1712 processVrEvent(event.valueInt,event.device); 1713 break; 1714 case EVENT_TYPE_ANSWER_CALL: 1715 //TODO(BT) could answer call happen on Connected state? 1716 processAnswerCall(event.device); 1717 break; 1718 case EVENT_TYPE_HANGUP_CALL: 1719 // TODO(BT) could hangup call happen on Connected state? 1720 processHangupCall(event.device); 1721 break; 1722 case EVENT_TYPE_VOLUME_CHANGED: 1723 processVolumeEvent(event.valueInt, event.valueInt2, 1724 event.device); 1725 break; 1726 case EVENT_TYPE_DIAL_CALL: 1727 processDialCall(event.valueString, event.device); 1728 break; 1729 case EVENT_TYPE_SEND_DTMF: 1730 processSendDtmf(event.valueInt, event.device); 1731 break; 1732 case EVENT_TYPE_NOICE_REDUCTION: 1733 processNoiceReductionEvent(event.valueInt, event.device); 1734 break; 1735 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 1736 processSubscriberNumberRequest(event.device); 1737 break; 1738 case EVENT_TYPE_AT_CIND: 1739 processAtCind(event.device); 1740 break; 1741 case EVENT_TYPE_AT_CHLD: 1742 processAtChld(event.valueInt, event.device); 1743 break; 1744 case EVENT_TYPE_AT_COPS: 1745 processAtCops(event.device); 1746 break; 1747 case EVENT_TYPE_AT_CLCC: 1748 processAtClcc(event.device); 1749 break; 1750 case EVENT_TYPE_UNKNOWN_AT: 1751 processUnknownAt(event.valueString,event.device); 1752 break; 1753 case EVENT_TYPE_KEY_PRESSED: 1754 processKeyPressed(event.device); 1755 break; 1756 default: 1757 Log.e(TAG, "Unexpected event: " + event.type); 1758 break; 1759 } 1760 break; 1761 default: 1762 return NOT_HANDLED; 1763 } 1764 return retValue; 1765 } 1766 1767 // in MultiHFPending state 1768 private void processConnectionEvent(int state, BluetoothDevice device) { 1769 Log.d(TAG, "processConnectionEvent state = " + state + 1770 ", device = " + device); 1771 switch (state) { 1772 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 1773 if (mConnectedDevicesList.contains(device)) { 1774 if (mMultiDisconnectDevice != null && 1775 mMultiDisconnectDevice.equals(device)) { 1776 mMultiDisconnectDevice = null; 1777 1778 synchronized (HeadsetStateMachine.this) { 1779 mConnectedDevicesList.remove(device); 1780 mHeadsetAudioParam.remove(device); 1781 mHeadsetBrsf.remove(device); 1782 Log.d(TAG, "device " + device.getAddress() + 1783 " is removed in MultiHFPending state"); 1784 broadcastConnectionState(device, 1785 BluetoothProfile.STATE_DISCONNECTED, 1786 BluetoothProfile.STATE_DISCONNECTING); 1787 } 1788 1789 if (mTargetDevice != null) { 1790 if (!connectHfpNative(getByteAddress(mTargetDevice))) { 1791 1792 broadcastConnectionState(mTargetDevice, 1793 BluetoothProfile.STATE_DISCONNECTED, 1794 BluetoothProfile.STATE_CONNECTING); 1795 synchronized (HeadsetStateMachine.this) { 1796 mTargetDevice = null; 1797 if (mConnectedDevicesList.size() == 0) { 1798 // Should be not in this state since it has at least 1799 // one HF connected in MultiHFPending state 1800 Log.d(TAG, "Should be not in this state, error handling"); 1801 transitionTo(mDisconnected); 1802 } 1803 else { 1804 processMultiHFConnected(device); 1805 } 1806 } 1807 } 1808 } else { 1809 synchronized (HeadsetStateMachine.this) { 1810 mIncomingDevice = null; 1811 if (mConnectedDevicesList.size() == 0) { 1812 transitionTo(mDisconnected); 1813 } 1814 else { 1815 processMultiHFConnected(device); 1816 } 1817 } 1818 } 1819 } else { 1820 /* Another HF disconnected when one HF is connecting */ 1821 synchronized (HeadsetStateMachine.this) { 1822 mConnectedDevicesList.remove(device); 1823 mHeadsetAudioParam.remove(device); 1824 mHeadsetBrsf.remove(device); 1825 Log.d(TAG, "device " + device.getAddress() + 1826 " is removed in MultiHFPending state"); 1827 } 1828 broadcastConnectionState(device, 1829 BluetoothProfile.STATE_DISCONNECTED, 1830 BluetoothProfile.STATE_CONNECTED); 1831 } 1832 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 1833 1834 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 1835 BluetoothProfile.STATE_CONNECTING); 1836 synchronized (HeadsetStateMachine.this) { 1837 mTargetDevice = null; 1838 if (mConnectedDevicesList.size() == 0) { 1839 transitionTo(mDisconnected); 1840 } 1841 else 1842 { 1843 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) 1844 transitionTo(mAudioOn); 1845 else transitionTo(mConnected); 1846 } 1847 } 1848 } else { 1849 Log.e(TAG, "Unknown device Disconnected: " + device); 1850 } 1851 break; 1852 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 1853 /* Outgoing disconnection for device failed */ 1854 if (mConnectedDevicesList.contains(device)) { 1855 1856 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1857 BluetoothProfile.STATE_DISCONNECTING); 1858 if (mTargetDevice != null) { 1859 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 1860 BluetoothProfile.STATE_CONNECTING); 1861 } 1862 synchronized (HeadsetStateMachine.this) { 1863 mTargetDevice = null; 1864 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) 1865 transitionTo(mAudioOn); 1866 else transitionTo(mConnected); 1867 } 1868 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 1869 1870 synchronized (HeadsetStateMachine.this) { 1871 mCurrentDevice = device; 1872 mConnectedDevicesList.add(device); 1873 Log.d(TAG, "device " + device.getAddress() + 1874 " is added in MultiHFPending state"); 1875 mTargetDevice = null; 1876 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) 1877 transitionTo(mAudioOn); 1878 else transitionTo(mConnected); 1879 } 1880 1881 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1882 BluetoothProfile.STATE_CONNECTING); 1883 configAudioParameters(device); 1884 } else { 1885 Log.w(TAG, "Some other incoming HF connected" + 1886 "in Multi Pending state"); 1887 if (okToConnect(device) && 1888 (mConnectedDevicesList.size() < max_hf_connections)) { 1889 Log.i(TAG,"Incoming Hf accepted"); 1890 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1891 BluetoothProfile.STATE_DISCONNECTED); 1892 synchronized (HeadsetStateMachine.this) { 1893 if (!mConnectedDevicesList.contains(device)) { 1894 mCurrentDevice = device; 1895 mConnectedDevicesList.add(device); 1896 Log.d(TAG, "device " + device.getAddress() + 1897 " is added in MultiHFPending state"); 1898 } 1899 } 1900 configAudioParameters(device); 1901 } else { 1902 // reject the connection and stay in Pending state itself 1903 Log.i(TAG,"Incoming Hf rejected. priority=" + 1904 mService.getPriority(device) + 1905 " bondState=" + device.getBondState()); 1906 disconnectHfpNative(getByteAddress(device)); 1907 // the other profile connection should be initiated 1908 AdapterService adapterService = AdapterService.getAdapterService(); 1909 if (adapterService != null) { 1910 adapterService.connectOtherProfile(device, 1911 AdapterService.PROFILE_CONN_REJECTED); 1912 } 1913 } 1914 } 1915 break; 1916 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 1917 if (mConnectedDevicesList.contains(device)) { 1918 Log.e(TAG, "current device tries to connect back"); 1919 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 1920 if (DBG) { 1921 log("Stack and target device are connecting"); 1922 } 1923 } 1924 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 1925 Log.e(TAG, "Another connecting event on" + 1926 "the incoming device"); 1927 } 1928 break; 1929 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 1930 if (mConnectedDevicesList.contains(device)) { 1931 if (DBG) { 1932 log("stack is disconnecting mCurrentDevice"); 1933 } 1934 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 1935 Log.e(TAG, "TargetDevice is getting disconnected"); 1936 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 1937 Log.e(TAG, "IncomingDevice is getting disconnected"); 1938 } else { 1939 Log.e(TAG, "Disconnecting unknow device: " + device); 1940 } 1941 break; 1942 default: 1943 Log.e(TAG, "Incorrect state: " + state); 1944 break; 1945 } 1946 } 1947 1948 private void processAudioEvent(int state, BluetoothDevice device) { 1949 if (!mConnectedDevicesList.contains(device)) { 1950 Log.e(TAG, "Audio changed on disconnected device: " + device); 1951 return; 1952 } 1953 1954 switch (state) { 1955 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 1956 if (!isScoAcceptable()) { 1957 Log.e(TAG,"Audio Connected without any listener"); 1958 disconnectAudioNative(getByteAddress(device)); 1959 break; 1960 } 1961 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED; 1962 setAudioParameters(device); /* Set proper Audio Parameters. */ 1963 mAudioManager.setBluetoothScoOn(true); 1964 mActiveScoDevice = device; 1965 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED, 1966 BluetoothHeadset.STATE_AUDIO_CONNECTING); 1967 /* The state should be still in MultiHFPending state when 1968 audio connected since other device is still connecting/ 1969 disconnecting */ 1970 break; 1971 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 1972 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING; 1973 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING, 1974 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1975 break; 1976 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1977 if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1978 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 1979 mAudioManager.setBluetoothScoOn(false); 1980 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 1981 BluetoothHeadset.STATE_AUDIO_CONNECTED); 1982 } 1983 /* The state should be still in MultiHFPending state when audio 1984 disconnected since other device is still connecting/ 1985 disconnecting */ 1986 break; 1987 1988 default: 1989 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1990 break; 1991 } 1992 } 1993 1994 private void processMultiHFConnected(BluetoothDevice device) { 1995 log("MultiHFPending state: processMultiHFConnected"); 1996 if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) { 1997 log ("mActiveScoDevice is disconnected, setting it to null"); 1998 mActiveScoDevice = null; 1999 } 2000 /* Assign the current activedevice again if the disconnected 2001 device equals to the current active device */ 2002 if (mCurrentDevice != null && mCurrentDevice.equals(device)) { 2003 int deviceSize = mConnectedDevicesList.size(); 2004 mCurrentDevice = mConnectedDevicesList.get(deviceSize-1); 2005 } 2006 // The disconnected device is not current active device 2007 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) 2008 transitionTo(mAudioOn); 2009 else transitionTo(mConnected); 2010 log("processMultiHFConnected , the latest mCurrentDevice is:" 2011 + mCurrentDevice); 2012 log("MultiHFPending state: processMultiHFConnected ," + 2013 "fake broadcasting for mCurrentDevice"); 2014 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 2015 BluetoothProfile.STATE_DISCONNECTED); 2016 } 2017 2018 } 2019 2020 2021 private ServiceConnection mConnection = new ServiceConnection() { 2022 public void onServiceConnected(ComponentName className, IBinder service) { 2023 if (DBG) Log.d(TAG, "Proxy object connected"); 2024 mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service); 2025 } 2026 2027 public void onServiceDisconnected(ComponentName className) { 2028 if (DBG) Log.d(TAG, "Proxy object disconnected"); 2029 mPhoneProxy = null; 2030 } 2031 }; 2032 2033 // HFP Connection state of the device could be changed by the state machine 2034 // in separate thread while this method is executing. 2035 int getConnectionState(BluetoothDevice device) { 2036 if (getCurrentState() == mDisconnected) { 2037 if (DBG) Log.d(TAG, "currentState is Disconnected"); 2038 return BluetoothProfile.STATE_DISCONNECTED; 2039 } 2040 2041 synchronized (this) { 2042 IState currentState = getCurrentState(); 2043 if (DBG) Log.d(TAG, "currentState = " + currentState); 2044 if (currentState == mPending) { 2045 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 2046 return BluetoothProfile.STATE_CONNECTING; 2047 } 2048 if (mConnectedDevicesList.contains(device)) { 2049 return BluetoothProfile.STATE_DISCONNECTING; 2050 } 2051 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 2052 return BluetoothProfile.STATE_CONNECTING; // incoming connection 2053 } 2054 return BluetoothProfile.STATE_DISCONNECTED; 2055 } 2056 2057 if (currentState == mMultiHFPending) { 2058 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 2059 return BluetoothProfile.STATE_CONNECTING; 2060 } 2061 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 2062 return BluetoothProfile.STATE_CONNECTING; // incoming connection 2063 } 2064 if (mConnectedDevicesList.contains(device)) { 2065 if ((mMultiDisconnectDevice != null) && 2066 (!mMultiDisconnectDevice.equals(device))) { 2067 // The device is still connected 2068 return BluetoothProfile.STATE_CONNECTED; 2069 } 2070 return BluetoothProfile.STATE_DISCONNECTING; 2071 } 2072 return BluetoothProfile.STATE_DISCONNECTED; 2073 } 2074 2075 if (currentState == mConnected || currentState == mAudioOn) { 2076 if (mConnectedDevicesList.contains(device)) { 2077 return BluetoothProfile.STATE_CONNECTED; 2078 } 2079 return BluetoothProfile.STATE_DISCONNECTED; 2080 } else { 2081 Log.e(TAG, "Bad currentState: " + currentState); 2082 return BluetoothProfile.STATE_DISCONNECTED; 2083 } 2084 } 2085 } 2086 2087 List<BluetoothDevice> getConnectedDevices() { 2088 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 2089 synchronized(this) { 2090 for (int i = 0; i < mConnectedDevicesList.size(); i++) 2091 devices.add(mConnectedDevicesList.get(i)); 2092 } 2093 2094 return devices; 2095 } 2096 2097 boolean isAudioOn() { 2098 return (getCurrentState() == mAudioOn); 2099 } 2100 2101 boolean isAudioConnected(BluetoothDevice device) { 2102 synchronized(this) { 2103 2104 /* Additional check for audio state included for the case when PhoneApp queries 2105 Bluetooth Audio state, before we receive the close event from the stack for the 2106 sco disconnect issued in AudioOn state. This was causing a mismatch in the 2107 Incall screen UI. */ 2108 2109 if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device) 2110 && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) 2111 { 2112 return true; 2113 } 2114 } 2115 return false; 2116 } 2117 2118 int getAudioState(BluetoothDevice device) { 2119 synchronized(this) { 2120 if (mConnectedDevicesList.size() == 0) { 2121 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 2122 } 2123 } 2124 return mAudioState; 2125 } 2126 2127 private void processVrEvent(int state, BluetoothDevice device) { 2128 2129 if(device == null) { 2130 Log.w(TAG, "processVrEvent device is null"); 2131 return; 2132 } 2133 Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " + 2134 mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " + mWaitingForVoiceRecognition + 2135 " isInCall: " + isInCall()); 2136 if (state == HeadsetHalConstants.VR_STATE_STARTED) { 2137 if (!isVirtualCallInProgress() && 2138 !isInCall()) 2139 { 2140 try { 2141 mService.startActivity(sVoiceCommandIntent); 2142 } catch (ActivityNotFoundException e) { 2143 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 2144 0, getByteAddress(device)); 2145 return; 2146 } 2147 expectVoiceRecognition(device); 2148 } 2149 } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) { 2150 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) 2151 { 2152 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 2153 0, getByteAddress(device)); 2154 mVoiceRecognitionStarted = false; 2155 mWaitingForVoiceRecognition = false; 2156 if (!isInCall() && (mActiveScoDevice != null)) { 2157 disconnectAudioNative(getByteAddress(mActiveScoDevice)); 2158 mAudioManager.setParameters("A2dpSuspended=false"); 2159 } 2160 } 2161 else 2162 { 2163 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 2164 0, getByteAddress(device)); 2165 } 2166 } else { 2167 Log.e(TAG, "Bad Voice Recognition state: " + state); 2168 } 2169 } 2170 2171 private void processLocalVrEvent(int state) 2172 { 2173 BluetoothDevice device = null; 2174 if (state == HeadsetHalConstants.VR_STATE_STARTED) 2175 { 2176 boolean needAudio = true; 2177 if (mVoiceRecognitionStarted || isInCall()) 2178 { 2179 Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() + 2180 " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 2181 return; 2182 } 2183 mVoiceRecognitionStarted = true; 2184 2185 if (mWaitingForVoiceRecognition) 2186 { 2187 device = getDeviceForMessage(START_VR_TIMEOUT); 2188 if (device == null) 2189 return; 2190 2191 Log.d(TAG, "Voice recognition started successfully"); 2192 mWaitingForVoiceRecognition = false; 2193 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 2194 0, getByteAddress(device)); 2195 removeMessages(START_VR_TIMEOUT); 2196 } 2197 else 2198 { 2199 Log.d(TAG, "Voice recognition started locally"); 2200 needAudio = startVoiceRecognitionNative(getByteAddress(mCurrentDevice)); 2201 if (mCurrentDevice != null) 2202 device = mCurrentDevice; 2203 } 2204 2205 if (needAudio && !isAudioOn()) 2206 { 2207 Log.d(TAG, "Initiating audio connection for Voice Recognition"); 2208 // At this stage, we need to be sure that AVDTP is not streaming. This is needed 2209 // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in 2210 // streaming state while a SCO connection is established. 2211 // This is needed for VoiceDial scenario alone and not for 2212 // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE 2213 // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed. 2214 // Whereas for VoiceDial we want to activate the SCO connection but we are still 2215 // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream 2216 mAudioManager.setParameters("A2dpSuspended=true"); 2217 connectAudioNative(getByteAddress(device)); 2218 } 2219 2220 if (mStartVoiceRecognitionWakeLock.isHeld()) { 2221 mStartVoiceRecognitionWakeLock.release(); 2222 } 2223 } 2224 else 2225 { 2226 Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " + mVoiceRecognitionStarted + 2227 " mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition); 2228 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) 2229 { 2230 mVoiceRecognitionStarted = false; 2231 mWaitingForVoiceRecognition = false; 2232 2233 if (stopVoiceRecognitionNative(getByteAddress(mCurrentDevice)) 2234 && !isInCall() && mActiveScoDevice != null) { 2235 disconnectAudioNative(getByteAddress(mActiveScoDevice)); 2236 mAudioManager.setParameters("A2dpSuspended=false"); 2237 } 2238 } 2239 } 2240 } 2241 2242 private synchronized void expectVoiceRecognition(BluetoothDevice device) { 2243 mWaitingForVoiceRecognition = true; 2244 Message m = obtainMessage(START_VR_TIMEOUT); 2245 m.obj = getMatchingDevice(device); 2246 sendMessageDelayed(m, START_VR_TIMEOUT_VALUE); 2247 2248 if (!mStartVoiceRecognitionWakeLock.isHeld()) { 2249 mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE); 2250 } 2251 } 2252 2253 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 2254 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 2255 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 2256 int connectionState; 2257 synchronized (this) { 2258 for (BluetoothDevice device : bondedDevices) { 2259 ParcelUuid[] featureUuids = device.getUuids(); 2260 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 2261 continue; 2262 } 2263 connectionState = getConnectionState(device); 2264 for(int i = 0; i < states.length; i++) { 2265 if (connectionState == states[i]) { 2266 deviceList.add(device); 2267 } 2268 } 2269 } 2270 } 2271 return deviceList; 2272 } 2273 2274 private BluetoothDevice getDeviceForMessage(int what) 2275 { 2276 if (what == CONNECT_TIMEOUT) { 2277 log("getDeviceForMessage: returning mTargetDevice for what=" + what); 2278 return mTargetDevice; 2279 } 2280 if (mConnectedDevicesList.size() == 0) { 2281 log("getDeviceForMessage: No connected device. what=" + what); 2282 return null; 2283 } 2284 for (BluetoothDevice device : mConnectedDevicesList) 2285 { 2286 if (getHandler().hasMessages(what, device)) 2287 { 2288 log("getDeviceForMessage: returning " + device); 2289 return device; 2290 } 2291 } 2292 log("getDeviceForMessage: No matching device for " + what + ". Returning null"); 2293 return null; 2294 } 2295 2296 private BluetoothDevice getMatchingDevice(BluetoothDevice device) 2297 { 2298 for (BluetoothDevice matchingDevice : mConnectedDevicesList) 2299 { 2300 if (matchingDevice.equals(device)) 2301 { 2302 return matchingDevice; 2303 } 2304 } 2305 return null; 2306 } 2307 2308 // This method does not check for error conditon (newState == prevState) 2309 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 2310 log("Connection state " + device + ": " + prevState + "->" + newState); 2311 if(prevState == BluetoothProfile.STATE_CONNECTED) { 2312 // Headset is disconnecting, stop Virtual call if active. 2313 terminateScoUsingVirtualVoiceCall(); 2314 } 2315 2316 /* Notifying the connection state change of the profile before sending the intent for 2317 connection state change, as it was causing a race condition, with the UI not being 2318 updated with the correct connection state. */ 2319 mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET, 2320 newState, prevState); 2321 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 2322 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 2323 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 2324 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2325 mService.sendBroadcastAsUser(intent, UserHandle.ALL, 2326 HeadsetService.BLUETOOTH_PERM); 2327 } 2328 2329 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 2330 if(prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 2331 // When SCO gets disconnected during call transfer, Virtual call 2332 //needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall. 2333 terminateScoUsingVirtualVoiceCall(); 2334 } 2335 Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 2336 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 2337 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 2338 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2339 mService.sendBroadcastAsUser(intent, UserHandle.ALL, 2340 HeadsetService.BLUETOOTH_PERM); 2341 log("Audio state " + device + ": " + prevState + "->" + newState); 2342 } 2343 2344 /* 2345 * Put the AT command, company ID, arguments, and device in an Intent and broadcast it. 2346 */ 2347 private void broadcastVendorSpecificEventIntent(String command, 2348 int companyId, 2349 int commandType, 2350 Object[] arguments, 2351 BluetoothDevice device) { 2352 log("broadcastVendorSpecificEventIntent(" + command + ")"); 2353 Intent intent = 2354 new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT); 2355 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command); 2356 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, 2357 commandType); 2358 // assert: all elements of args are Serializable 2359 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments); 2360 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2361 2362 intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY 2363 + "." + Integer.toString(companyId)); 2364 2365 mService.sendBroadcastAsUser(intent, UserHandle.ALL, 2366 HeadsetService.BLUETOOTH_PERM); 2367 } 2368 2369 private void configAudioParameters(BluetoothDevice device) 2370 { 2371 // Reset NREC on connect event. Headset will override later 2372 HashMap<String, Integer> AudioParamConfig = new HashMap<String, Integer>(); 2373 AudioParamConfig.put("NREC", 1); 2374 mHeadsetAudioParam.put(device, AudioParamConfig); 2375 mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device) + ";" + 2376 HEADSET_NREC + "=on"); 2377 Log.d(TAG, "configAudioParameters for device:" + device + " are: nrec = " + 2378 AudioParamConfig.get("NREC")); 2379 } 2380 2381 private void setAudioParameters(BluetoothDevice device) 2382 { 2383 // 1. update nrec value 2384 // 2. update headset name 2385 HashMap<String, Integer> AudioParam = mHeadsetAudioParam.get(device); 2386 int mNrec = AudioParam.get("NREC"); 2387 2388 if (mNrec == 1) { 2389 Log.d(TAG, "Set NREC: 1 for device:" + device); 2390 mAudioManager.setParameters(HEADSET_NREC + "=on"); 2391 } else { 2392 Log.d(TAG, "Set NREC: 0 for device:" + device); 2393 mAudioManager.setParameters(HEADSET_NREC + "=off"); 2394 } 2395 mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device)); 2396 } 2397 2398 private String parseUnknownAt(String atString) 2399 { 2400 StringBuilder atCommand = new StringBuilder(atString.length()); 2401 String result = null; 2402 2403 for (int i = 0; i < atString.length(); i++) { 2404 char c = atString.charAt(i); 2405 if (c == '"') { 2406 int j = atString.indexOf('"', i + 1 ); // search for closing " 2407 if (j == -1) { // unmatched ", insert one. 2408 atCommand.append(atString.substring(i, atString.length())); 2409 atCommand.append('"'); 2410 break; 2411 } 2412 atCommand.append(atString.substring(i, j + 1)); 2413 i = j; 2414 } else if (c != ' ') { 2415 atCommand.append(Character.toUpperCase(c)); 2416 } 2417 } 2418 result = atCommand.toString(); 2419 return result; 2420 } 2421 2422 private int getAtCommandType(String atCommand) 2423 { 2424 int commandType = mPhonebook.TYPE_UNKNOWN; 2425 String atString = null; 2426 atCommand = atCommand.trim(); 2427 if (atCommand.length() > 5) 2428 { 2429 atString = atCommand.substring(5); 2430 if (atString.startsWith("?")) // Read 2431 commandType = mPhonebook.TYPE_READ; 2432 else if (atString.startsWith("=?")) // Test 2433 commandType = mPhonebook.TYPE_TEST; 2434 else if (atString.startsWith("=")) // Set 2435 commandType = mPhonebook.TYPE_SET; 2436 else 2437 commandType = mPhonebook.TYPE_UNKNOWN; 2438 } 2439 return commandType; 2440 } 2441 2442 /* Method to check if Virtual Call in Progress */ 2443 private boolean isVirtualCallInProgress() { 2444 return mVirtualCallStarted; 2445 } 2446 2447 void setVirtualCallInProgress(boolean state) { 2448 mVirtualCallStarted = state; 2449 } 2450 2451 /* NOTE: Currently the VirtualCall API does not support handling of 2452 call transfers. If it is initiated from the handsfree device, 2453 HeadsetStateMachine will end the virtual call by calling 2454 terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */ 2455 synchronized boolean initiateScoUsingVirtualVoiceCall() { 2456 if (DBG) log("initiateScoUsingVirtualVoiceCall: Received"); 2457 // 1. Check if the SCO state is idle 2458 if (isInCall() || mVoiceRecognitionStarted) { 2459 Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress."); 2460 return false; 2461 } 2462 2463 // 2. Send virtual phone state changed to initialize SCO 2464 processCallState(new HeadsetCallState(0, 0, 2465 HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true); 2466 processCallState(new HeadsetCallState(0, 0, 2467 HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true); 2468 processCallState(new HeadsetCallState(1, 0, 2469 HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true); 2470 setVirtualCallInProgress(true); 2471 // Done 2472 if (DBG) log("initiateScoUsingVirtualVoiceCall: Done"); 2473 return true; 2474 } 2475 2476 synchronized boolean terminateScoUsingVirtualVoiceCall() { 2477 if (DBG) log("terminateScoUsingVirtualVoiceCall: Received"); 2478 2479 if (!isVirtualCallInProgress()) { 2480 Log.e(TAG, "terminateScoUsingVirtualVoiceCall:"+ 2481 "No present call to terminate"); 2482 return false; 2483 } 2484 2485 // 2. Send virtual phone state changed to close SCO 2486 processCallState(new HeadsetCallState(0, 0, 2487 HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true); 2488 setVirtualCallInProgress(false); 2489 // Done 2490 if (DBG) log("terminateScoUsingVirtualVoiceCall: Done"); 2491 return true; 2492 } 2493 2494 private void processAnswerCall(BluetoothDevice device) { 2495 if(device == null) { 2496 Log.w(TAG, "processAnswerCall device is null"); 2497 return; 2498 } 2499 2500 if (mPhoneProxy != null) { 2501 try { 2502 mPhoneProxy.answerCall(); 2503 } catch (RemoteException e) { 2504 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2505 } 2506 } else { 2507 Log.e(TAG, "Handsfree phone proxy null for answering call"); 2508 } 2509 } 2510 2511 private void processHangupCall(BluetoothDevice device) { 2512 if(device == null) { 2513 Log.w(TAG, "processHangupCall device is null"); 2514 return; 2515 } 2516 // Close the virtual call if active. Virtual call should be 2517 // terminated for CHUP callback event 2518 if (isVirtualCallInProgress()) { 2519 terminateScoUsingVirtualVoiceCall(); 2520 } else { 2521 if (mPhoneProxy != null) { 2522 try { 2523 mPhoneProxy.hangupCall(); 2524 } catch (RemoteException e) { 2525 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2526 } 2527 } else { 2528 Log.e(TAG, "Handsfree phone proxy null for hanging up call"); 2529 } 2530 } 2531 } 2532 2533 private void processDialCall(String number, BluetoothDevice device) { 2534 if(device == null) { 2535 Log.w(TAG, "processDialCall device is null"); 2536 return; 2537 } 2538 2539 String dialNumber; 2540 if (mDialingOut) { 2541 if (DBG) log("processDialCall, already dialling"); 2542 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, 2543 getByteAddress(device)); 2544 return; 2545 } 2546 if ((number == null) || (number.length() == 0)) { 2547 dialNumber = mPhonebook.getLastDialledNumber(); 2548 if (dialNumber == null) { 2549 if (DBG) log("processDialCall, last dial number null"); 2550 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, 2551 getByteAddress(device)); 2552 return; 2553 } 2554 } else if (number.charAt(0) == '>') { 2555 // Yuck - memory dialling requested. 2556 // Just dial last number for now 2557 if (number.startsWith(">9999")) { // for PTS test 2558 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, 2559 getByteAddress(device)); 2560 return; 2561 } 2562 if (DBG) log("processDialCall, memory dial do last dial for now"); 2563 dialNumber = mPhonebook.getLastDialledNumber(); 2564 if (dialNumber == null) { 2565 if (DBG) log("processDialCall, last dial number null"); 2566 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, 2567 getByteAddress(device)); 2568 return; 2569 } 2570 } else { 2571 // Remove trailing ';' 2572 if (number.charAt(number.length() - 1) == ';') { 2573 number = number.substring(0, number.length() - 1); 2574 } 2575 2576 dialNumber = PhoneNumberUtils.convertPreDial(number); 2577 } 2578 // Check for virtual call to terminate before sending Call Intent 2579 terminateScoUsingVirtualVoiceCall(); 2580 2581 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 2582 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 2583 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2584 mService.startActivity(intent); 2585 // TODO(BT) continue send OK reults code after call starts 2586 // hold wait lock, start a timer, set wait call flag 2587 // Get call started indication from bluetooth phone 2588 mDialingOut = true; 2589 Message m = obtainMessage(DIALING_OUT_TIMEOUT); 2590 m.obj = getMatchingDevice(device); 2591 sendMessageDelayed(m, DIALING_OUT_TIMEOUT_VALUE); 2592 } 2593 2594 private void processVolumeEvent(int volumeType, int volume, BluetoothDevice device) { 2595 if(device != null && !device.equals(mActiveScoDevice) && mPhoneState.isInCall()) { 2596 Log.w(TAG, "ignore processVolumeEvent"); 2597 return; 2598 } 2599 2600 if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) { 2601 mPhoneState.setSpeakerVolume(volume); 2602 int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0; 2603 mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag); 2604 } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) { 2605 mPhoneState.setMicVolume(volume); 2606 } else { 2607 Log.e(TAG, "Bad voluem type: " + volumeType); 2608 } 2609 } 2610 2611 private void processSendDtmf(int dtmf, BluetoothDevice device) { 2612 if(device == null) { 2613 Log.w(TAG, "processSendDtmf device is null"); 2614 return; 2615 } 2616 2617 if (mPhoneProxy != null) { 2618 try { 2619 mPhoneProxy.sendDtmf(dtmf); 2620 } catch (RemoteException e) { 2621 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2622 } 2623 } else { 2624 Log.e(TAG, "Handsfree phone proxy null for sending DTMF"); 2625 } 2626 } 2627 2628 private void processCallState(HeadsetCallState callState) { 2629 processCallState(callState, false); 2630 } 2631 2632 private void processCallState(HeadsetCallState callState, 2633 boolean isVirtualCall) { 2634 mPhoneState.setNumActiveCall(callState.mNumActive); 2635 mPhoneState.setNumHeldCall(callState.mNumHeld); 2636 mPhoneState.setCallState(callState.mCallState); 2637 if (mDialingOut) { 2638 if (callState.mCallState == 2639 HeadsetHalConstants.CALL_STATE_DIALING) { 2640 BluetoothDevice device = getDeviceForMessage(DIALING_OUT_TIMEOUT); 2641 if (device == null) { 2642 return; 2643 } 2644 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 2645 0, getByteAddress(device)); 2646 removeMessages(DIALING_OUT_TIMEOUT); 2647 } else if (callState.mCallState == 2648 HeadsetHalConstants.CALL_STATE_ACTIVE || callState.mCallState 2649 == HeadsetHalConstants.CALL_STATE_IDLE) { 2650 mDialingOut = false; 2651 } 2652 } 2653 2654 /* Set ActiveScoDevice to null when call ends */ 2655 if ((mActiveScoDevice != null) && !isInCall() && 2656 callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE) 2657 mActiveScoDevice = null; 2658 2659 log("mNumActive: " + callState.mNumActive + " mNumHeld: " + 2660 callState.mNumHeld +" mCallState: " + callState.mCallState); 2661 log("mNumber: " + callState.mNumber + " mType: " + callState.mType); 2662 2663 if (isVirtualCall) { 2664 // virtual call state update 2665 if (getCurrentState() != mDisconnected) { 2666 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 2667 callState.mCallState, callState.mNumber, callState.mType); 2668 } 2669 } else { 2670 // circuit-switch voice call update 2671 // stop virtual voice call if there is a CSV call ongoing 2672 if (callState.mNumActive > 0 || callState.mNumHeld > 0 2673 || callState.mCallState != HeadsetHalConstants.CALL_STATE_IDLE) { 2674 terminateScoUsingVirtualVoiceCall(); 2675 } 2676 2677 // Specific handling for case of starting MO/MT call while VOIP 2678 // ongoing, terminateScoUsingVirtualVoiceCall() resets callState 2679 // INCOMING/DIALING to IDLE. Some HS send AT+CIND? to read call 2680 // and get wrong value of callsetup. This case is hit only 2681 // SCO for VOIP call is not terminated via SDK API call. 2682 if (mPhoneState.getCallState() != callState.mCallState) { 2683 mPhoneState.setCallState(callState.mCallState); 2684 } 2685 2686 // at this step: if there is virtual call ongoing, it means there is no CSV call 2687 // let virtual call continue and skip phone state update 2688 if (!isVirtualCallInProgress()) { 2689 if (getCurrentState() != mDisconnected) { 2690 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 2691 callState.mCallState, callState.mNumber, callState.mType); 2692 } 2693 } 2694 } 2695 } 2696 2697 // 1 enable noice reduction 2698 // 0 disable noice reduction 2699 private void processNoiceReductionEvent(int enable, BluetoothDevice device) { 2700 HashMap<String, Integer> AudioParamNrec = mHeadsetAudioParam.get(device); 2701 if (enable == 1) 2702 AudioParamNrec.put("NREC", 1); 2703 else 2704 AudioParamNrec.put("NREC", 0); 2705 Log.d(TAG, "NREC value for device :" + device + " is: " + AudioParamNrec.get("NREC")); 2706 } 2707 2708 // 2 - WBS on 2709 // 1 - NBS on 2710 private void processWBSEvent(int enable, BluetoothDevice device) { 2711 if (enable == 2) { 2712 Log.d(TAG, "AudioManager.setParameters bt_wbs=on for " + 2713 device.getName() + " - " + device.getAddress()); 2714 mAudioManager.setParameters(HEADSET_WBS + "=on"); 2715 } else { 2716 Log.d(TAG, "AudioManager.setParameters bt_wbs=off for " + 2717 device.getName() + " - " + device.getAddress()); 2718 mAudioManager.setParameters(HEADSET_WBS + "=off"); 2719 } 2720 } 2721 2722 private void processAtChld(int chld, BluetoothDevice device) { 2723 if(device == null) { 2724 Log.w(TAG, "processAtChld device is null"); 2725 return; 2726 } 2727 2728 if (mPhoneProxy != null) { 2729 try { 2730 if (mPhoneProxy.processChld(chld)) { 2731 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 2732 0, getByteAddress(device)); 2733 } else { 2734 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 2735 0, getByteAddress(device)); 2736 } 2737 } catch (RemoteException e) { 2738 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2739 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 2740 0, getByteAddress(device)); 2741 } 2742 } else { 2743 Log.e(TAG, "Handsfree phone proxy null for At+Chld"); 2744 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 2745 0, getByteAddress(device)); 2746 } 2747 } 2748 2749 private void processSubscriberNumberRequest(BluetoothDevice device) { 2750 if(device == null) { 2751 Log.w(TAG, "processSubscriberNumberRequest device is null"); 2752 return; 2753 } 2754 2755 if (mPhoneProxy != null) { 2756 try { 2757 String number = mPhoneProxy.getSubscriberNumber(); 2758 if (number != null) { 2759 atResponseStringNative("+CNUM: ,\"" + number + "\"," + 2760 PhoneNumberUtils.toaFromString(number) + 2761 ",,4", getByteAddress(device)); 2762 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 2763 0, getByteAddress(device)); 2764 } else { 2765 Log.e(TAG, "getSubscriberNumber returns null"); 2766 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 2767 0, getByteAddress(device)); 2768 } 2769 } catch (RemoteException e) { 2770 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2771 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 2772 0, getByteAddress(device)); 2773 } 2774 } else { 2775 Log.e(TAG, "Handsfree phone proxy null for At+CNUM"); 2776 } 2777 } 2778 2779 private void processAtCind(BluetoothDevice device) { 2780 int call, call_setup; 2781 2782 if(device == null) { 2783 Log.w(TAG, "processAtCind device is null"); 2784 return; 2785 } 2786 2787 /* Handsfree carkits expect that +CIND is properly responded to 2788 Hence we ensure that a proper response is sent 2789 for the virtual call too.*/ 2790 if (isVirtualCallInProgress()) { 2791 call = 1; 2792 call_setup = 0; 2793 } else { 2794 // regular phone call 2795 call = mPhoneState.getNumActiveCall(); 2796 call_setup = mPhoneState.getNumHeldCall(); 2797 } 2798 2799 cindResponseNative(mPhoneState.getService(), call, 2800 call_setup, mPhoneState.getCallState(), 2801 mPhoneState.getSignal(), mPhoneState.getRoam(), 2802 mPhoneState.getBatteryCharge(), getByteAddress(device)); 2803 } 2804 2805 private void processAtCops(BluetoothDevice device) { 2806 if(device == null) { 2807 Log.w(TAG, "processAtCops device is null"); 2808 return; 2809 } 2810 2811 if (mPhoneProxy != null) { 2812 try { 2813 String operatorName = mPhoneProxy.getNetworkOperator(); 2814 if (operatorName == null) { 2815 operatorName = ""; 2816 } 2817 copsResponseNative(operatorName, getByteAddress(device)); 2818 } catch (RemoteException e) { 2819 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2820 copsResponseNative("", getByteAddress(device)); 2821 } 2822 } else { 2823 Log.e(TAG, "Handsfree phone proxy null for At+COPS"); 2824 copsResponseNative("", getByteAddress(device)); 2825 } 2826 } 2827 2828 private void processAtClcc(BluetoothDevice device) { 2829 if(device == null) { 2830 Log.w(TAG, "processAtClcc device is null"); 2831 return; 2832 } 2833 2834 if (mPhoneProxy != null) { 2835 try { 2836 if(isVirtualCallInProgress()) { 2837 String phoneNumber = ""; 2838 int type = PhoneNumberUtils.TOA_Unknown; 2839 try { 2840 phoneNumber = mPhoneProxy.getSubscriberNumber(); 2841 type = PhoneNumberUtils.toaFromString(phoneNumber); 2842 } catch (RemoteException ee) { 2843 Log.e(TAG, "Unable to retrieve phone number"+ 2844 "using IBluetoothHeadsetPhone proxy"); 2845 phoneNumber = ""; 2846 } 2847 clccResponseNative(1, 0, 0, 0, false, phoneNumber, type, 2848 getByteAddress(device)); 2849 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2850 } 2851 else if (!mPhoneProxy.listCurrentCalls()) { 2852 clccResponseNative(0, 0, 0, 0, false, "", 0, 2853 getByteAddress(device)); 2854 } 2855 else 2856 { 2857 Log.d(TAG, "Starting CLCC response timeout for device: " 2858 + device); 2859 Message m = obtainMessage(CLCC_RSP_TIMEOUT); 2860 m.obj = getMatchingDevice(device); 2861 sendMessageDelayed(m, CLCC_RSP_TIMEOUT_VALUE); 2862 } 2863 } catch (RemoteException e) { 2864 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2865 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2866 } 2867 } else { 2868 Log.e(TAG, "Handsfree phone proxy null for At+CLCC"); 2869 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device)); 2870 } 2871 } 2872 2873 private void processAtCscs(String atString, int type, BluetoothDevice device) { 2874 log("processAtCscs - atString = "+ atString); 2875 if(mPhonebook != null) { 2876 mPhonebook.handleCscsCommand(atString, type, device); 2877 } 2878 else { 2879 Log.e(TAG, "Phonebook handle null for At+CSCS"); 2880 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2881 } 2882 } 2883 2884 private void processAtCpbs(String atString, int type, BluetoothDevice device) { 2885 log("processAtCpbs - atString = "+ atString); 2886 if(mPhonebook != null) { 2887 mPhonebook.handleCpbsCommand(atString, type, device); 2888 } 2889 else { 2890 Log.e(TAG, "Phonebook handle null for At+CPBS"); 2891 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2892 } 2893 } 2894 2895 private void processAtCpbr(String atString, int type, BluetoothDevice device) { 2896 log("processAtCpbr - atString = "+ atString); 2897 if(mPhonebook != null) { 2898 mPhonebook.handleCpbrCommand(atString, type, device); 2899 } 2900 else { 2901 Log.e(TAG, "Phonebook handle null for At+CPBR"); 2902 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 2903 } 2904 } 2905 2906 /** 2907 * Find a character ch, ignoring quoted sections. 2908 * Return input.length() if not found. 2909 */ 2910 static private int findChar(char ch, String input, int fromIndex) { 2911 for (int i = fromIndex; i < input.length(); i++) { 2912 char c = input.charAt(i); 2913 if (c == '"') { 2914 i = input.indexOf('"', i + 1); 2915 if (i == -1) { 2916 return input.length(); 2917 } 2918 } else if (c == ch) { 2919 return i; 2920 } 2921 } 2922 return input.length(); 2923 } 2924 2925 /** 2926 * Break an argument string into individual arguments (comma delimited). 2927 * Integer arguments are turned into Integer objects. Otherwise a String 2928 * object is used. 2929 */ 2930 static private Object[] generateArgs(String input) { 2931 int i = 0; 2932 int j; 2933 ArrayList<Object> out = new ArrayList<Object>(); 2934 while (i <= input.length()) { 2935 j = findChar(',', input, i); 2936 2937 String arg = input.substring(i, j); 2938 try { 2939 out.add(new Integer(arg)); 2940 } catch (NumberFormatException e) { 2941 out.add(arg); 2942 } 2943 2944 i = j + 1; // move past comma 2945 } 2946 return out.toArray(); 2947 } 2948 2949 /** 2950 * @return {@code true} if the given string is a valid vendor-specific AT command. 2951 */ 2952 private boolean processVendorSpecificAt(String atString) { 2953 log("processVendorSpecificAt - atString = " + atString); 2954 2955 // Currently we accept only SET type commands. 2956 int indexOfEqual = atString.indexOf("="); 2957 if (indexOfEqual == -1) { 2958 Log.e(TAG, "processVendorSpecificAt: command type error in " + atString); 2959 return false; 2960 } 2961 2962 String command = atString.substring(0, indexOfEqual); 2963 Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command); 2964 if (companyId == null) { 2965 Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString); 2966 return false; 2967 } 2968 2969 String arg = atString.substring(indexOfEqual + 1); 2970 if (arg.startsWith("?")) { 2971 Log.e(TAG, "processVendorSpecificAt: command type error in " + atString); 2972 return false; 2973 } 2974 2975 Object[] args = generateArgs(arg); 2976 broadcastVendorSpecificEventIntent(command, 2977 companyId, 2978 BluetoothHeadset.AT_CMD_TYPE_SET, 2979 args, 2980 mCurrentDevice); 2981 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(mCurrentDevice)); 2982 return true; 2983 } 2984 2985 private void processUnknownAt(String atString, BluetoothDevice device) { 2986 if(device == null) { 2987 Log.w(TAG, "processUnknownAt device is null"); 2988 return; 2989 } 2990 2991 // TODO (BT) 2992 log("processUnknownAt - atString = "+ atString); 2993 String atCommand = parseUnknownAt(atString); 2994 int commandType = getAtCommandType(atCommand); 2995 if (atCommand.startsWith("+CSCS")) 2996 processAtCscs(atCommand.substring(5), commandType, device); 2997 else if (atCommand.startsWith("+CPBS")) 2998 processAtCpbs(atCommand.substring(5), commandType, device); 2999 else if (atCommand.startsWith("+CPBR")) 3000 processAtCpbr(atCommand.substring(5), commandType, device); 3001 else if (!processVendorSpecificAt(atCommand)) 3002 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device)); 3003 } 3004 3005 private void processKeyPressed(BluetoothDevice device) { 3006 if(device == null) { 3007 Log.w(TAG, "processKeyPressed device is null"); 3008 return; 3009 } 3010 3011 if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) { 3012 if (mPhoneProxy != null) { 3013 try { 3014 mPhoneProxy.answerCall(); 3015 } catch (RemoteException e) { 3016 Log.e(TAG, Log.getStackTraceString(new Throwable())); 3017 } 3018 } else { 3019 Log.e(TAG, "Handsfree phone proxy null for answering call"); 3020 } 3021 } else if (mPhoneState.getNumActiveCall() > 0) { 3022 if (!isAudioOn()) 3023 { 3024 connectAudioNative(getByteAddress(mCurrentDevice)); 3025 } 3026 else 3027 { 3028 if (mPhoneProxy != null) { 3029 try { 3030 mPhoneProxy.hangupCall(); 3031 } catch (RemoteException e) { 3032 Log.e(TAG, Log.getStackTraceString(new Throwable())); 3033 } 3034 } else { 3035 Log.e(TAG, "Handsfree phone proxy null for hangup call"); 3036 } 3037 } 3038 } else { 3039 String dialNumber = mPhonebook.getLastDialledNumber(); 3040 if (dialNumber == null) { 3041 if (DBG) log("processKeyPressed, last dial number null"); 3042 return; 3043 } 3044 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 3045 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 3046 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3047 mService.startActivity(intent); 3048 } 3049 } 3050 3051 private void onConnectionStateChanged(int state, byte[] address) { 3052 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 3053 event.valueInt = state; 3054 event.device = getDevice(address); 3055 sendMessage(STACK_EVENT, event); 3056 } 3057 3058 private void onAudioStateChanged(int state, byte[] address) { 3059 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 3060 event.valueInt = state; 3061 event.device = getDevice(address); 3062 sendMessage(STACK_EVENT, event); 3063 } 3064 3065 private void onVrStateChanged(int state, byte[] address) { 3066 StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED); 3067 event.valueInt = state; 3068 event.device = getDevice(address); 3069 sendMessage(STACK_EVENT, event); 3070 } 3071 3072 private void onAnswerCall(byte[] address) { 3073 StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL); 3074 event.device = getDevice(address); 3075 sendMessage(STACK_EVENT, event); 3076 } 3077 3078 private void onHangupCall(byte[] address) { 3079 StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL); 3080 event.device = getDevice(address); 3081 sendMessage(STACK_EVENT, event); 3082 } 3083 3084 private void onVolumeChanged(int type, int volume, byte[] address) { 3085 StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED); 3086 event.valueInt = type; 3087 event.valueInt2 = volume; 3088 event.device = getDevice(address); 3089 sendMessage(STACK_EVENT, event); 3090 } 3091 3092 private void onDialCall(String number, byte[] address) { 3093 StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL); 3094 event.valueString = number; 3095 event.device = getDevice(address); 3096 sendMessage(STACK_EVENT, event); 3097 } 3098 3099 private void onSendDtmf(int dtmf, byte[] address) { 3100 StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF); 3101 event.valueInt = dtmf; 3102 event.device = getDevice(address); 3103 sendMessage(STACK_EVENT, event); 3104 } 3105 3106 private void onNoiceReductionEnable(boolean enable, byte[] address) { 3107 StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION); 3108 event.valueInt = enable ? 1 : 0; 3109 event.device = getDevice(address); 3110 sendMessage(STACK_EVENT, event); 3111 } 3112 3113 private void onWBS(int codec, byte[] address) { 3114 StackEvent event = new StackEvent(EVENT_TYPE_WBS); 3115 event.valueInt = codec; 3116 event.device = getDevice(address); 3117 sendMessage(STACK_EVENT, event); 3118 } 3119 3120 private void onAtChld(int chld, byte[] address) { 3121 StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD); 3122 event.valueInt = chld; 3123 event.device = getDevice(address); 3124 sendMessage(STACK_EVENT, event); 3125 } 3126 3127 private void onAtCnum(byte[] address) { 3128 StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST); 3129 event.device = getDevice(address); 3130 sendMessage(STACK_EVENT, event); 3131 } 3132 3133 private void onAtCind(byte[] address) { 3134 StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND); 3135 event.device = getDevice(address); 3136 sendMessage(STACK_EVENT, event); 3137 } 3138 3139 private void onAtCops(byte[] address) { 3140 StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS); 3141 event.device = getDevice(address); 3142 sendMessage(STACK_EVENT, event); 3143 } 3144 3145 private void onAtClcc(byte[] address) { 3146 StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC); 3147 event.device = getDevice(address); 3148 sendMessage(STACK_EVENT, event); 3149 } 3150 3151 private void onUnknownAt(String atString, byte[] address) { 3152 StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT); 3153 event.valueString = atString; 3154 event.device = getDevice(address); 3155 sendMessage(STACK_EVENT, event); 3156 } 3157 3158 private void onKeyPressed(byte[] address) { 3159 StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED); 3160 event.device = getDevice(address); 3161 sendMessage(STACK_EVENT, event); 3162 } 3163 3164 private void processIntentBatteryChanged(Intent intent) { 3165 int batteryLevel = intent.getIntExtra("level", -1); 3166 int scale = intent.getIntExtra("scale", -1); 3167 if (batteryLevel == -1 || scale == -1 || scale == 0) { 3168 Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale); 3169 return; 3170 } 3171 batteryLevel = batteryLevel * 5 / scale; 3172 mPhoneState.setBatteryCharge(batteryLevel); 3173 } 3174 3175 private void processDeviceStateChanged(HeadsetDeviceState deviceState) { 3176 notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal, 3177 deviceState.mBatteryCharge); 3178 } 3179 3180 private void processSendClccResponse(HeadsetClccResponse clcc) { 3181 BluetoothDevice device = getDeviceForMessage(CLCC_RSP_TIMEOUT); 3182 if (device == null) { 3183 return; 3184 } 3185 if (clcc.mIndex == 0) { 3186 removeMessages(CLCC_RSP_TIMEOUT); 3187 } 3188 clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty, 3189 clcc.mNumber, clcc.mType, getByteAddress(device)); 3190 } 3191 3192 private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) { 3193 String stringToSend = resultCode.mCommand + ": "; 3194 if (resultCode.mArg != null) { 3195 stringToSend += resultCode.mArg; 3196 } 3197 atResponseStringNative(stringToSend, getByteAddress(resultCode.mDevice)); 3198 } 3199 3200 private String getCurrentDeviceName(BluetoothDevice device) { 3201 String defaultName = "<unknown>"; 3202 3203 if(device == null) { 3204 return defaultName; 3205 } 3206 3207 String deviceName = device.getName(); 3208 if (deviceName == null) { 3209 return defaultName; 3210 } 3211 return deviceName; 3212 } 3213 3214 private byte[] getByteAddress(BluetoothDevice device) { 3215 return Utils.getBytesFromAddress(device.getAddress()); 3216 } 3217 3218 private BluetoothDevice getDevice(byte[] address) { 3219 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 3220 } 3221 3222 private boolean isInCall() { 3223 return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) || 3224 (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE)); 3225 } 3226 3227 // Accept incoming SCO only when there is active call, VR activated, 3228 // active VOIP call 3229 private boolean isScoAcceptable() { 3230 return (mVoiceRecognitionStarted || isInCall()); 3231 } 3232 3233 boolean isConnected() { 3234 IState currentState = getCurrentState(); 3235 return (currentState == mConnected || currentState == mAudioOn); 3236 } 3237 3238 boolean okToConnect(BluetoothDevice device) { 3239 AdapterService adapterService = AdapterService.getAdapterService(); 3240 int priority = mService.getPriority(device); 3241 boolean ret = false; 3242 //check if this is an incoming connection in Quiet mode. 3243 if((adapterService == null) || 3244 ((adapterService.isQuietModeEnabled() == true) && 3245 (mTargetDevice == null))){ 3246 ret = false; 3247 } 3248 // check priority and accept or reject the connection. if priority is undefined 3249 // it is likely that our SDP has not completed and peer is initiating the 3250 // connection. Allow this connection, provided the device is bonded 3251 else if((BluetoothProfile.PRIORITY_OFF < priority) || 3252 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 3253 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 3254 ret= true; 3255 } 3256 return ret; 3257 } 3258 3259 @Override 3260 protected void log(String msg) { 3261 if (DBG) { 3262 super.log(msg); 3263 } 3264 } 3265 3266 public void handleAccessPermissionResult(Intent intent) { 3267 log("handleAccessPermissionResult"); 3268 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 3269 if (mPhonebook != null) { 3270 if (!mPhonebook.getCheckingAccessPermission()) { 3271 return; 3272 } 3273 int atCommandResult = 0; 3274 int atCommandErrorCode = 0; 3275 //HeadsetBase headset = mHandsfree.getHeadset(); 3276 // ASSERT: (headset != null) && headSet.isConnected() 3277 // REASON: mCheckingAccessPermission is true, otherwise resetAtState 3278 // has set mCheckingAccessPermission to false 3279 if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 3280 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 3281 BluetoothDevice.CONNECTION_ACCESS_NO) 3282 == BluetoothDevice.CONNECTION_ACCESS_YES) { 3283 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 3284 mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); 3285 } 3286 atCommandResult = mPhonebook.processCpbrCommand(device); 3287 } else { 3288 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 3289 mCurrentDevice.setPhonebookAccessPermission( 3290 BluetoothDevice.ACCESS_REJECTED); 3291 } 3292 } 3293 } 3294 mPhonebook.setCpbrIndex(-1); 3295 mPhonebook.setCheckingAccessPermission(false); 3296 3297 if (atCommandResult >= 0) { 3298 atResponseCodeNative(atCommandResult, atCommandErrorCode, getByteAddress(device)); 3299 } else { 3300 log("handleAccessPermissionResult - RESULT_NONE"); 3301 } 3302 } else { 3303 Log.e(TAG, "Phonebook handle null"); 3304 if (device != null) { 3305 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, 3306 getByteAddress(device)); 3307 } 3308 } 3309 } 3310 3311 private static final String SCHEME_TEL = "tel"; 3312 3313 // Event types for STACK_EVENT message 3314 final private static int EVENT_TYPE_NONE = 0; 3315 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 3316 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 3317 final private static int EVENT_TYPE_VR_STATE_CHANGED = 3; 3318 final private static int EVENT_TYPE_ANSWER_CALL = 4; 3319 final private static int EVENT_TYPE_HANGUP_CALL = 5; 3320 final private static int EVENT_TYPE_VOLUME_CHANGED = 6; 3321 final private static int EVENT_TYPE_DIAL_CALL = 7; 3322 final private static int EVENT_TYPE_SEND_DTMF = 8; 3323 final private static int EVENT_TYPE_NOICE_REDUCTION = 9; 3324 final private static int EVENT_TYPE_AT_CHLD = 10; 3325 final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11; 3326 final private static int EVENT_TYPE_AT_CIND = 12; 3327 final private static int EVENT_TYPE_AT_COPS = 13; 3328 final private static int EVENT_TYPE_AT_CLCC = 14; 3329 final private static int EVENT_TYPE_UNKNOWN_AT = 15; 3330 final private static int EVENT_TYPE_KEY_PRESSED = 16; 3331 final private static int EVENT_TYPE_WBS = 17; 3332 3333 private class StackEvent { 3334 int type = EVENT_TYPE_NONE; 3335 int valueInt = 0; 3336 int valueInt2 = 0; 3337 String valueString = null; 3338 BluetoothDevice device = null; 3339 3340 private StackEvent(int type) { 3341 this.type = type; 3342 } 3343 } 3344 3345 /*package*/native boolean atResponseCodeNative(int responseCode, int errorCode, 3346 byte[] address); 3347 /*package*/ native boolean atResponseStringNative(String responseString, byte[] address); 3348 3349 private native static void classInitNative(); 3350 private native void initializeNative(int max_hf_clients); 3351 private native void cleanupNative(); 3352 private native boolean connectHfpNative(byte[] address); 3353 private native boolean disconnectHfpNative(byte[] address); 3354 private native boolean connectAudioNative(byte[] address); 3355 private native boolean disconnectAudioNative(byte[] address); 3356 private native boolean startVoiceRecognitionNative(byte[] address); 3357 private native boolean stopVoiceRecognitionNative(byte[] address); 3358 private native boolean setVolumeNative(int volumeType, int volume, byte[] address); 3359 private native boolean cindResponseNative(int service, int numActive, int numHeld, 3360 int callState, int signal, int roam, 3361 int batteryCharge, byte[] address); 3362 private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal, 3363 int batteryCharge); 3364 3365 private native boolean clccResponseNative(int index, int dir, int status, int mode, 3366 boolean mpty, String number, int type, 3367 byte[] address); 3368 private native boolean copsResponseNative(String operatorName, byte[] address); 3369 3370 private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState, 3371 String number, int type); 3372 private native boolean configureWBSNative(byte[] address,int condec_config); 3373} 3374