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