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