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