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