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