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