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