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