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