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