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