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