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