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