HeadsetStateMachine.java revision 44d87716fccf8ff6f114db72711388fa57068078
1/* 2 * Copyright (C) 2012 Google Inc. 3 */ 4 5/** 6 * Bluetooth Handset StateMachine 7 * (Disconnected) 8 * | ^ 9 * CONNECT | | DISCONNECTED 10 * V | 11 * (Pending) 12 * | ^ 13 * CONNECTED | | CONNECT 14 * V | 15 * (Connected) 16 * | ^ 17 * CONNECT_AUDIO | | DISCONNECT_AUDIO 18 * V | 19 * (AudioOn) 20 */ 21package com.android.bluetooth.hfp; 22 23import android.bluetooth.BluetoothAdapter; 24import android.bluetooth.BluetoothDevice; 25import android.bluetooth.BluetoothHeadset; 26import android.bluetooth.BluetoothProfile; 27import android.bluetooth.BluetoothUuid; 28import android.bluetooth.IBluetooth; 29import android.bluetooth.IBluetoothHeadsetPhone; 30import android.content.ComponentName; 31import android.content.Context; 32import android.content.Intent; 33import android.content.ServiceConnection; 34import android.content.ActivityNotFoundException; 35import android.media.AudioManager; 36import android.net.Uri; 37import android.os.IBinder; 38import android.os.Message; 39import android.os.ParcelUuid; 40import android.os.RemoteException; 41import android.os.ServiceManager; 42import android.os.PowerManager; 43import android.os.PowerManager.WakeLock; 44import android.telephony.PhoneNumberUtils; 45import android.util.Log; 46import com.android.bluetooth.Utils; 47import com.android.bluetooth.btservice.AdapterService; 48import com.android.internal.util.IState; 49import com.android.internal.util.State; 50import com.android.internal.util.StateMachine; 51import java.util.ArrayList; 52import java.util.List; 53import java.util.Set; 54 55final class HeadsetStateMachine extends StateMachine { 56 private static final String TAG = "HeadsetStateMachine"; 57 private static final boolean DBG = true; 58 //For Debugging only 59 private static int sRefCount=0; 60 61 private static final String HEADSET_NAME = "bt_headset_name"; 62 private static final String HEADSET_NREC = "bt_headset_nrec"; 63 64 static final int CONNECT = 1; 65 static final int DISCONNECT = 2; 66 static final int CONNECT_AUDIO = 3; 67 static final int DISCONNECT_AUDIO = 4; 68 static final int VOICE_RECOGNITION_START = 5; 69 static final int VOICE_RECOGNITION_STOP = 6; 70 71 // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION 72 // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO 73 static final int INTENT_SCO_VOLUME_CHANGED = 7; 74 static final int SET_MIC_VOLUME = 8; 75 static final int CALL_STATE_CHANGED = 9; 76 static final int INTENT_BATTERY_CHANGED = 10; 77 static final int DEVICE_STATE_CHANGED = 11; 78 static final int ROAM_CHANGED = 12; 79 static final int SEND_CCLC_RESPONSE = 13; 80 81 static final int VIRTUAL_CALL_START = 14; 82 static final int VIRTUAL_CALL_STOP = 15; 83 84 private static final int STACK_EVENT = 101; 85 private static final int DIALING_OUT_TIMEOUT = 102; 86 private static final int START_VR_TIMEOUT = 103; 87 88 private static final int CONNECT_TIMEOUT = 201; 89 90 private static final int DIALING_OUT_TIMEOUT_VALUE = 10000; 91 private static final int START_VR_TIMEOUT_VALUE = 5000; 92 93 private static final ParcelUuid[] HEADSET_UUIDS = { 94 BluetoothUuid.HSP, 95 BluetoothUuid.Handsfree, 96 }; 97 98 private Disconnected mDisconnected; 99 private Pending mPending; 100 private Connected mConnected; 101 private AudioOn mAudioOn; 102 103 private HeadsetService mService; 104 private PowerManager mPowerManager; 105 private boolean mVirtualCallStarted = false; 106 private boolean mVoiceRecognitionStarted = false; 107 private boolean mWaitingForVoiceRecognition = false; 108 private WakeLock mStartVoiceRecognitionWakeLock; // held while waiting for voice recognition 109 110 private boolean mDialingOut = false; 111 private AudioManager mAudioManager; 112 private AtPhonebook mPhonebook; 113 114 private static Intent sVoiceCommandIntent; 115 116 private HeadsetPhoneState mPhoneState; 117 private int mAudioState; 118 private BluetoothAdapter mAdapter; 119 private IBluetoothHeadsetPhone mPhoneProxy; 120 private boolean mNativeAvailable; 121 122 // mCurrentDevice is the device connected before the state changes 123 // mTargetDevice is the device to be connected 124 // mIncomingDevice is the device connecting to us, valid only in Pending state 125 // when mIncomingDevice is not null, both mCurrentDevice 126 // and mTargetDevice are null 127 // when either mCurrentDevice or mTargetDevice is not null, 128 // mIncomingDevice is null 129 // Stable states 130 // No connection, Disconnected state 131 // both mCurrentDevice and mTargetDevice are null 132 // Connected, Connected state 133 // mCurrentDevice is not null, mTargetDevice is null 134 // Interim states 135 // Connecting to a device, Pending 136 // mCurrentDevice is null, mTargetDevice is not null 137 // Disconnecting device, Connecting to new device 138 // Pending 139 // Both mCurrentDevice and mTargetDevice are not null 140 // Disconnecting device Pending 141 // mCurrentDevice is not null, mTargetDevice is null 142 // Incoming connections Pending 143 // Both mCurrentDevice and mTargetDevice are null 144 private BluetoothDevice mCurrentDevice = null; 145 private BluetoothDevice mTargetDevice = null; 146 private BluetoothDevice mIncomingDevice = null; 147 148 static { 149 classInitNative(); 150 } 151 152 HeadsetStateMachine(HeadsetService context) { 153 super(TAG); 154 mService = context; 155 mVoiceRecognitionStarted = false; 156 mWaitingForVoiceRecognition = false; 157 158 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 159 mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 160 TAG + ":VoiceRecognition"); 161 mStartVoiceRecognitionWakeLock.setReferenceCounted(false); 162 163 mDialingOut = false; 164 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 165 mPhonebook = new AtPhonebook(mService, this); 166 mPhoneState = new HeadsetPhoneState(context, this); 167 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 168 mAdapter = BluetoothAdapter.getDefaultAdapter(); 169 if (!context.bindService(new Intent(IBluetoothHeadsetPhone.class.getName()), 170 mConnection, 0)) { 171 Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service"); 172 } 173 174 initializeNative(); 175 mNativeAvailable=true; 176 177 mDisconnected = new Disconnected(); 178 mPending = new Pending(); 179 mConnected = new Connected(); 180 mAudioOn = new AudioOn(); 181 182 if (sVoiceCommandIntent == null) { 183 sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND); 184 sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 185 } 186 187 addState(mDisconnected); 188 addState(mPending); 189 addState(mConnected); 190 addState(mAudioOn); 191 192 setInitialState(mDisconnected); 193 } 194 195 196 public void doQuit() { 197 quitNow(); 198 } 199 200 public void cleanup() { 201 if (mPhoneProxy != null) { 202 if (DBG) Log.d(TAG,"Unbinding service..."); 203 synchronized (mConnection) { 204 try { 205 mPhoneProxy = null; 206 mService.unbindService(mConnection); 207 } catch (Exception re) { 208 Log.e(TAG,"Error unbinding from IBluetoothHeadsetPhone",re); 209 } 210 } 211 } 212 if (mPhoneState != null) { 213 mPhoneState.listenForPhoneState(false); 214 mPhoneState.cleanup(); 215 } 216 if (mPhonebook != null) { 217 mPhonebook.cleanup(); 218 } 219 if (mNativeAvailable) { 220 cleanupNative(); 221 mNativeAvailable = false; 222 } 223 } 224 225 private class Disconnected extends State { 226 @Override 227 public void enter() { 228 log("Enter Disconnected: " + getCurrentMessage().what); 229 mPhonebook.resetAtState(); 230 mPhoneState.listenForPhoneState(false); 231 } 232 233 @Override 234 public boolean processMessage(Message message) { 235 log("Disconnected process message: " + message.what); 236 if (DBG) { 237 if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) { 238 log("ERROR: current, target, or mIncomingDevice not null in Disconnected"); 239 return NOT_HANDLED; 240 } 241 } 242 243 boolean retValue = HANDLED; 244 switch(message.what) { 245 case CONNECT: 246 BluetoothDevice device = (BluetoothDevice) message.obj; 247 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 248 BluetoothProfile.STATE_DISCONNECTED); 249 250 if (!connectHfpNative(getByteAddress(device)) ) { 251 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 252 BluetoothProfile.STATE_CONNECTING); 253 break; 254 } 255 256 synchronized (HeadsetStateMachine.this) { 257 mTargetDevice = device; 258 transitionTo(mPending); 259 } 260 // TODO(BT) remove CONNECT_TIMEOUT when the stack 261 // sends back events consistently 262 sendMessageDelayed(CONNECT_TIMEOUT, 30000); 263 break; 264 case DISCONNECT: 265 // ignore 266 break; 267 case INTENT_BATTERY_CHANGED: 268 processIntentBatteryChanged((Intent) message.obj); 269 break; 270 case ROAM_CHANGED: 271 processRoamChanged((Boolean) message.obj); 272 break; 273 case CALL_STATE_CHANGED: 274 processCallState((HeadsetCallState) message.obj, 275 ((message.arg1 == 1)?true:false)); 276 break; 277 case STACK_EVENT: 278 StackEvent event = (StackEvent) message.obj; 279 if (DBG) { 280 log("event type: " + event.type); 281 } 282 switch (event.type) { 283 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 284 processConnectionEvent(event.valueInt, event.device); 285 break; 286 default: 287 Log.e(TAG, "Unexpected stack event: " + event.type); 288 break; 289 } 290 break; 291 default: 292 return NOT_HANDLED; 293 } 294 return retValue; 295 } 296 297 @Override 298 public void exit() { 299 log("Exit Disconnected: " + getCurrentMessage().what); 300 } 301 302 // in Disconnected state 303 private void processConnectionEvent(int state, BluetoothDevice device) { 304 int priority; 305 switch (state) { 306 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 307 Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device); 308 break; 309 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 310 // check priority and accept or reject the connection. if priority is undefined 311 // it is likely that our SDP has not completed and peer is initiating the 312 // connection. Allow this connection, provided the device is bonded 313 // Since the state changes to Connecting or directly Connected in some cases.Have the check both in 314 // CONNECTION_STATE_CONNECTING and CONNECTION_STATE_CONNECTED. 315 priority = mService.getPriority(device); 316 if ((BluetoothProfile.PRIORITY_OFF < priority) || 317 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 318 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 319 Log.i(TAG,"Incoming Hf accepted"); 320 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 321 BluetoothProfile.STATE_DISCONNECTED); 322 synchronized (HeadsetStateMachine.this) { 323 mIncomingDevice = device; 324 transitionTo(mPending); 325 } 326 } else { 327 Log.i(TAG,"Incoming Hf rejected. priority=" + priority + 328 " bondState=" + device.getBondState()); 329 //reject the connection and stay in Disconnected state itself 330 disconnectHfpNative(getByteAddress(device)); 331 // the other profile connection should be initiated 332 AdapterService adapterService = AdapterService.getAdapterService(); 333 if ( adapterService != null) { 334 adapterService.connectOtherProfile(device, 335 AdapterService.PROFILE_CONN_REJECTED); 336 } 337 } 338 break; 339 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 340 Log.w(TAG, "HFP Connected from Disconnected state"); 341 // check priority and accept or reject the connection. if priority is undefined 342 // it is likely that our SDP has not completed and peer is initiating the 343 // connection. Allow this connection, provided the device is bonded 344 priority = mService.getPriority(device); 345 if ((BluetoothProfile.PRIORITY_OFF < priority) || 346 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 347 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 348 Log.i(TAG,"Incoming Hf accepted"); 349 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 350 BluetoothProfile.STATE_DISCONNECTED); 351 synchronized (HeadsetStateMachine.this) { 352 mCurrentDevice = device; 353 transitionTo(mConnected); 354 } 355 configAudioParameters(); 356 } else { 357 //reject the connection and stay in Disconnected state itself 358 Log.i(TAG,"Incoming Hf rejected. priority=" + priority + 359 " bondState=" + device.getBondState()); 360 disconnectHfpNative(getByteAddress(device)); 361 // the other profile connection should be initiated 362 AdapterService adapterService = AdapterService.getAdapterService(); 363 if ( adapterService != null) { 364 adapterService.connectOtherProfile(device, 365 AdapterService.PROFILE_CONN_REJECTED); 366 } 367 } 368 break; 369 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 370 Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device); 371 break; 372 default: 373 Log.e(TAG, "Incorrect state: " + state); 374 break; 375 } 376 } 377 } 378 379 private class Pending extends State { 380 @Override 381 public void enter() { 382 log("Enter Pending: " + getCurrentMessage().what); 383 } 384 385 @Override 386 public boolean processMessage(Message message) { 387 log("Pending process message: " + message.what); 388 389 boolean retValue = HANDLED; 390 switch(message.what) { 391 case CONNECT: 392 case CONNECT_AUDIO: 393 deferMessage(message); 394 break; 395 case CONNECT_TIMEOUT: 396 onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, 397 getByteAddress(mTargetDevice)); 398 break; 399 case DISCONNECT: 400 BluetoothDevice device = (BluetoothDevice) message.obj; 401 if (mCurrentDevice != null && mTargetDevice != null && 402 mTargetDevice.equals(device) ) { 403 // cancel connection to the mTargetDevice 404 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 405 BluetoothProfile.STATE_CONNECTING); 406 synchronized (HeadsetStateMachine.this) { 407 mTargetDevice = null; 408 } 409 } else { 410 deferMessage(message); 411 } 412 break; 413 case INTENT_BATTERY_CHANGED: 414 processIntentBatteryChanged((Intent) message.obj); 415 break; 416 case ROAM_CHANGED: 417 processRoamChanged((Boolean) message.obj); 418 break; 419 case CALL_STATE_CHANGED: 420 processCallState((HeadsetCallState) message.obj, 421 ((message.arg1 == 1)?true:false)); 422 break; 423 case STACK_EVENT: 424 StackEvent event = (StackEvent) message.obj; 425 if (DBG) { 426 log("event type: " + event.type); 427 } 428 switch (event.type) { 429 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 430 removeMessages(CONNECT_TIMEOUT); 431 processConnectionEvent(event.valueInt, event.device); 432 break; 433 default: 434 Log.e(TAG, "Unexpected event: " + event.type); 435 break; 436 } 437 break; 438 default: 439 return NOT_HANDLED; 440 } 441 return retValue; 442 } 443 444 // in Pending state 445 private void processConnectionEvent(int state, BluetoothDevice device) { 446 switch (state) { 447 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 448 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 449 broadcastConnectionState(mCurrentDevice, 450 BluetoothProfile.STATE_DISCONNECTED, 451 BluetoothProfile.STATE_DISCONNECTING); 452 synchronized (HeadsetStateMachine.this) { 453 mCurrentDevice = null; 454 } 455 456 if (mTargetDevice != null) { 457 if (!connectHfpNative(getByteAddress(mTargetDevice))) { 458 broadcastConnectionState(mTargetDevice, 459 BluetoothProfile.STATE_DISCONNECTED, 460 BluetoothProfile.STATE_CONNECTING); 461 synchronized (HeadsetStateMachine.this) { 462 mTargetDevice = null; 463 transitionTo(mDisconnected); 464 } 465 } 466 } else { 467 synchronized (HeadsetStateMachine.this) { 468 mIncomingDevice = null; 469 transitionTo(mDisconnected); 470 } 471 } 472 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 473 // outgoing connection failed 474 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 475 BluetoothProfile.STATE_CONNECTING); 476 synchronized (HeadsetStateMachine.this) { 477 mTargetDevice = null; 478 transitionTo(mDisconnected); 479 } 480 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 481 broadcastConnectionState(mIncomingDevice, 482 BluetoothProfile.STATE_DISCONNECTED, 483 BluetoothProfile.STATE_CONNECTING); 484 synchronized (HeadsetStateMachine.this) { 485 mIncomingDevice = null; 486 transitionTo(mDisconnected); 487 } 488 } else { 489 Log.e(TAG, "Unknown device Disconnected: " + device); 490 } 491 break; 492 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 493 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 494 // disconnection failed 495 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 496 BluetoothProfile.STATE_DISCONNECTING); 497 if (mTargetDevice != null) { 498 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 499 BluetoothProfile.STATE_CONNECTING); 500 } 501 synchronized (HeadsetStateMachine.this) { 502 mTargetDevice = null; 503 transitionTo(mConnected); 504 } 505 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 506 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 507 BluetoothProfile.STATE_CONNECTING); 508 synchronized (HeadsetStateMachine.this) { 509 mCurrentDevice = mTargetDevice; 510 mTargetDevice = null; 511 transitionTo(mConnected); 512 } 513 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 514 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 515 BluetoothProfile.STATE_CONNECTING); 516 synchronized (HeadsetStateMachine.this) { 517 mCurrentDevice = mIncomingDevice; 518 mIncomingDevice = null; 519 transitionTo(mConnected); 520 } 521 } else { 522 Log.e(TAG, "Unknown device Connected: " + device); 523 // something is wrong here, but sync our state with stack 524 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 525 BluetoothProfile.STATE_DISCONNECTED); 526 synchronized (HeadsetStateMachine.this) { 527 mCurrentDevice = device; 528 mTargetDevice = null; 529 mIncomingDevice = null; 530 transitionTo(mConnected); 531 } 532 } 533 configAudioParameters(); 534 break; 535 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 536 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 537 log("current device tries to connect back"); 538 // TODO(BT) ignore or reject 539 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 540 // The stack is connecting to target device or 541 // there is an incoming connection from the target device at the same time 542 // we already broadcasted the intent, doing nothing here 543 if (DBG) { 544 log("Stack and target device are connecting"); 545 } 546 } 547 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 548 Log.e(TAG, "Another connecting event on the incoming device"); 549 } else { 550 // We get an incoming connecting request while Pending 551 // TODO(BT) is stack handing this case? let's ignore it for now 552 log("Incoming connection while pending, ignore"); 553 } 554 break; 555 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 556 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 557 // we already broadcasted the intent, doing nothing here 558 if (DBG) { 559 log("stack is disconnecting mCurrentDevice"); 560 } 561 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 562 Log.e(TAG, "TargetDevice is getting disconnected"); 563 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 564 Log.e(TAG, "IncomingDevice is getting disconnected"); 565 } else { 566 Log.e(TAG, "Disconnecting unknow device: " + device); 567 } 568 break; 569 default: 570 Log.e(TAG, "Incorrect state: " + state); 571 break; 572 } 573 } 574 575 } 576 577 private class Connected extends State { 578 @Override 579 public void enter() { 580 log("Enter Connected: " + getCurrentMessage().what); 581 } 582 583 @Override 584 public boolean processMessage(Message message) { 585 log("Connected process message: " + message.what); 586 if (DBG) { 587 if (mCurrentDevice == null) { 588 log("ERROR: mCurrentDevice is null in Connected"); 589 return NOT_HANDLED; 590 } 591 } 592 593 boolean retValue = HANDLED; 594 switch(message.what) { 595 case CONNECT: 596 { 597 BluetoothDevice device = (BluetoothDevice) message.obj; 598 if (mCurrentDevice.equals(device)) { 599 break; 600 } 601 602 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 603 BluetoothProfile.STATE_DISCONNECTED); 604 if (!disconnectHfpNative(getByteAddress(mCurrentDevice))) { 605 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 606 BluetoothProfile.STATE_CONNECTING); 607 break; 608 } 609 610 synchronized (HeadsetStateMachine.this) { 611 mTargetDevice = device; 612 transitionTo(mPending); 613 } 614 } 615 break; 616 case DISCONNECT: 617 { 618 BluetoothDevice device = (BluetoothDevice) message.obj; 619 if (!mCurrentDevice.equals(device)) { 620 break; 621 } 622 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 623 BluetoothProfile.STATE_CONNECTED); 624 if (!disconnectHfpNative(getByteAddress(device))) { 625 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 626 BluetoothProfile.STATE_DISCONNECTED); 627 break; 628 } 629 transitionTo(mPending); 630 } 631 break; 632 case CONNECT_AUDIO: 633 // TODO(BT) when failure, broadcast audio connecting to disconnected intent 634 // check if device matches mCurrentDevice 635 connectAudioNative(getByteAddress(mCurrentDevice)); 636 break; 637 case VOICE_RECOGNITION_START: 638 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED); 639 break; 640 case VOICE_RECOGNITION_STOP: 641 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); 642 break; 643 case CALL_STATE_CHANGED: 644 processCallState((HeadsetCallState) message.obj, ((message.arg1==1)?true:false)); 645 break; 646 case INTENT_BATTERY_CHANGED: 647 processIntentBatteryChanged((Intent) message.obj); 648 break; 649 case ROAM_CHANGED: 650 processRoamChanged((Boolean) message.obj); 651 break; 652 case DEVICE_STATE_CHANGED: 653 processDeviceStateChanged((HeadsetDeviceState) message.obj); 654 break; 655 case SEND_CCLC_RESPONSE: 656 processSendClccResponse((HeadsetClccResponse) message.obj); 657 break; 658 case DIALING_OUT_TIMEOUT: 659 if (mDialingOut) { 660 mDialingOut= false; 661 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 662 } 663 break; 664 case VIRTUAL_CALL_START: 665 initiateScoUsingVirtualVoiceCall(); 666 break; 667 case VIRTUAL_CALL_STOP: 668 terminateScoUsingVirtualVoiceCall(); 669 break; 670 case START_VR_TIMEOUT: 671 if (mWaitingForVoiceRecognition) { 672 mWaitingForVoiceRecognition = false; 673 Log.e(TAG, "Timeout waiting for voice recognition to start"); 674 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 675 } 676 break; 677 case STACK_EVENT: 678 StackEvent event = (StackEvent) message.obj; 679 if (DBG) { 680 log("event type: " + event.type); 681 } 682 switch (event.type) { 683 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 684 processConnectionEvent(event.valueInt, event.device); 685 break; 686 case EVENT_TYPE_AUDIO_STATE_CHANGED: 687 processAudioEvent(event.valueInt, event.device); 688 break; 689 case EVENT_TYPE_VR_STATE_CHANGED: 690 processVrEvent(event.valueInt); 691 break; 692 case EVENT_TYPE_ANSWER_CALL: 693 // TODO(BT) could answer call happen on Connected state? 694 processAnswerCall(); 695 break; 696 case EVENT_TYPE_HANGUP_CALL: 697 // TODO(BT) could hangup call happen on Connected state? 698 processHangupCall(); 699 break; 700 case EVENT_TYPE_VOLUME_CHANGED: 701 processVolumeEvent(event.valueInt, event.valueInt2); 702 break; 703 case EVENT_TYPE_DIAL_CALL: 704 processDialCall(event.valueString); 705 break; 706 case EVENT_TYPE_SEND_DTMF: 707 processSendDtmf(event.valueInt); 708 break; 709 case EVENT_TYPE_NOICE_REDUCTION: 710 processNoiceReductionEvent(event.valueInt); 711 break; 712 case EVENT_TYPE_AT_CHLD: 713 processAtChld(event.valueInt); 714 break; 715 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 716 processSubscriberNumberRequest(); 717 break; 718 case EVENT_TYPE_AT_CIND: 719 processAtCind(); 720 break; 721 case EVENT_TYPE_AT_COPS: 722 processAtCops(); 723 break; 724 case EVENT_TYPE_AT_CLCC: 725 processAtClcc(); 726 break; 727 case EVENT_TYPE_UNKNOWN_AT: 728 processUnknownAt(event.valueString); 729 break; 730 case EVENT_TYPE_KEY_PRESSED: 731 processKeyPressed(); 732 break; 733 default: 734 Log.e(TAG, "Unknown stack event: " + event.type); 735 break; 736 } 737 break; 738 default: 739 return NOT_HANDLED; 740 } 741 return retValue; 742 } 743 744 // in Connected state 745 private void processConnectionEvent(int state, BluetoothDevice device) { 746 switch (state) { 747 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 748 if (mCurrentDevice.equals(device)) { 749 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 750 BluetoothProfile.STATE_CONNECTED); 751 synchronized (HeadsetStateMachine.this) { 752 mCurrentDevice = null; 753 transitionTo(mDisconnected); 754 } 755 } else { 756 Log.e(TAG, "Disconnected from unknown device: " + device); 757 } 758 break; 759 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 760 processSlcConnected(); 761 break; 762 default: 763 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 764 break; 765 } 766 } 767 768 // in Connected state 769 private void processAudioEvent(int state, BluetoothDevice device) { 770 if (!mCurrentDevice.equals(device)) { 771 Log.e(TAG, "Audio changed on disconnected device: " + device); 772 return; 773 } 774 775 switch (state) { 776 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 777 // TODO(BT) should I save the state for next broadcast as the prevState? 778 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED; 779 mAudioManager.setBluetoothScoOn(true); 780 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED, 781 BluetoothHeadset.STATE_AUDIO_CONNECTING); 782 transitionTo(mAudioOn); 783 break; 784 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 785 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING; 786 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING, 787 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 788 break; 789 // TODO(BT) process other states 790 default: 791 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 792 break; 793 } 794 } 795 796 private void processSlcConnected() { 797 if (mPhoneProxy != null) { 798 try { 799 // start phone state listener here, instead of on disconnected exit() 800 // On BT off, exitting SM sends a SM exit() call which incorrectly forces 801 // a listenForPhoneState(true). 802 // Additionally, no indicator updates should be sent prior to SLC setup 803 mPhoneState.listenForPhoneState(true); 804 mPhoneProxy.queryPhoneState(); 805 } catch (RemoteException e) { 806 Log.e(TAG, Log.getStackTraceString(new Throwable())); 807 } 808 } else { 809 Log.e(TAG, "Handsfree phone proxy null for query phone state"); 810 } 811 812 } 813 } 814 815 private class AudioOn extends State { 816 817 @Override 818 public void enter() { 819 log("Enter AudioOn: " + getCurrentMessage().what); 820 } 821 822 @Override 823 public boolean processMessage(Message message) { 824 log("AudioOn process message: " + message.what); 825 if (DBG) { 826 if (mCurrentDevice == null) { 827 log("ERROR: mCurrentDevice is null in AudioOn"); 828 return NOT_HANDLED; 829 } 830 } 831 832 boolean retValue = HANDLED; 833 switch(message.what) { 834 case DISCONNECT: 835 { 836 BluetoothDevice device = (BluetoothDevice) message.obj; 837 if (!mCurrentDevice.equals(device)) { 838 break; 839 } 840 deferMessage(obtainMessage(DISCONNECT, message.obj)); 841 } 842 // fall through 843 case DISCONNECT_AUDIO: 844 if (disconnectAudioNative(getByteAddress(mCurrentDevice))) { 845 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 846 mAudioManager.setBluetoothScoOn(false); 847 broadcastAudioState(mCurrentDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 848 BluetoothHeadset.STATE_AUDIO_CONNECTED); 849 } 850 break; 851 case VOICE_RECOGNITION_START: 852 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED); 853 break; 854 case VOICE_RECOGNITION_STOP: 855 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); 856 break; 857 case INTENT_SCO_VOLUME_CHANGED: 858 processIntentScoVolume((Intent) message.obj); 859 break; 860 case CALL_STATE_CHANGED: 861 processCallState((HeadsetCallState) message.obj, ((message.arg1 == 1)?true:false)); 862 break; 863 case INTENT_BATTERY_CHANGED: 864 processIntentBatteryChanged((Intent) message.obj); 865 break; 866 case ROAM_CHANGED: 867 processRoamChanged((Boolean) message.obj); 868 break; 869 case DEVICE_STATE_CHANGED: 870 processDeviceStateChanged((HeadsetDeviceState) message.obj); 871 break; 872 case SEND_CCLC_RESPONSE: 873 processSendClccResponse((HeadsetClccResponse) message.obj); 874 break; 875 876 case VIRTUAL_CALL_START: 877 initiateScoUsingVirtualVoiceCall(); 878 break; 879 case VIRTUAL_CALL_STOP: 880 terminateScoUsingVirtualVoiceCall(); 881 break; 882 883 case DIALING_OUT_TIMEOUT: 884 if (mDialingOut) { 885 mDialingOut= false; 886 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 887 } 888 break; 889 case START_VR_TIMEOUT: 890 if (mWaitingForVoiceRecognition) { 891 mWaitingForVoiceRecognition = false; 892 Log.e(TAG, "Timeout waiting for voice recognition to start"); 893 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 894 } 895 break; 896 case STACK_EVENT: 897 StackEvent event = (StackEvent) message.obj; 898 if (DBG) { 899 log("event type: " + event.type); 900 } 901 switch (event.type) { 902 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 903 processConnectionEvent(event.valueInt, event.device); 904 break; 905 case EVENT_TYPE_AUDIO_STATE_CHANGED: 906 processAudioEvent(event.valueInt, event.device); 907 break; 908 case EVENT_TYPE_VR_STATE_CHANGED: 909 processVrEvent(event.valueInt); 910 break; 911 case EVENT_TYPE_ANSWER_CALL: 912 processAnswerCall(); 913 break; 914 case EVENT_TYPE_HANGUP_CALL: 915 processHangupCall(); 916 break; 917 case EVENT_TYPE_VOLUME_CHANGED: 918 processVolumeEvent(event.valueInt, event.valueInt2); 919 break; 920 case EVENT_TYPE_DIAL_CALL: 921 processDialCall(event.valueString); 922 break; 923 case EVENT_TYPE_SEND_DTMF: 924 processSendDtmf(event.valueInt); 925 break; 926 case EVENT_TYPE_NOICE_REDUCTION: 927 processNoiceReductionEvent(event.valueInt); 928 break; 929 case EVENT_TYPE_AT_CHLD: 930 processAtChld(event.valueInt); 931 break; 932 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 933 processSubscriberNumberRequest(); 934 break; 935 case EVENT_TYPE_AT_CIND: 936 processAtCind(); 937 break; 938 case EVENT_TYPE_AT_COPS: 939 processAtCops(); 940 break; 941 case EVENT_TYPE_AT_CLCC: 942 processAtClcc(); 943 break; 944 case EVENT_TYPE_UNKNOWN_AT: 945 processUnknownAt(event.valueString); 946 break; 947 case EVENT_TYPE_KEY_PRESSED: 948 processKeyPressed(); 949 break; 950 default: 951 Log.e(TAG, "Unknown stack event: " + event.type); 952 break; 953 } 954 break; 955 default: 956 return NOT_HANDLED; 957 } 958 return retValue; 959 } 960 961 // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this 962 private void processConnectionEvent(int state, BluetoothDevice device) { 963 switch (state) { 964 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 965 if (mCurrentDevice.equals(device)) { 966 processAudioEvent (HeadsetHalConstants.AUDIO_STATE_DISCONNECTED, device); 967 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 968 BluetoothProfile.STATE_CONNECTED); 969 synchronized (HeadsetStateMachine.this) { 970 mCurrentDevice = null; 971 transitionTo(mDisconnected); 972 } 973 } else { 974 Log.e(TAG, "Disconnected from unknown device: " + device); 975 } 976 break; 977 default: 978 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 979 break; 980 } 981 } 982 983 // in AudioOn state 984 private void processAudioEvent(int state, BluetoothDevice device) { 985 if (!mCurrentDevice.equals(device)) { 986 Log.e(TAG, "Audio changed on disconnected device: " + device); 987 return; 988 } 989 990 switch (state) { 991 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 992 if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 993 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 994 mAudioManager.setBluetoothScoOn(false); 995 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 996 BluetoothHeadset.STATE_AUDIO_CONNECTED); 997 } 998 transitionTo(mConnected); 999 break; 1000 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1001 // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset? 1002 //broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING, 1003 // BluetoothHeadset.STATE_AUDIO_CONNECTED); 1004 break; 1005 default: 1006 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1007 break; 1008 } 1009 } 1010 1011 private void processIntentScoVolume(Intent intent) { 1012 int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 1013 if (mPhoneState.getSpeakerVolume() != volumeValue) { 1014 mPhoneState.setSpeakerVolume(volumeValue); 1015 setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue); 1016 } 1017 } 1018 } 1019 1020 private ServiceConnection mConnection = new ServiceConnection() { 1021 public void onServiceConnected(ComponentName className, IBinder service) { 1022 if (DBG) Log.d(TAG, "Proxy object connected"); 1023 mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service); 1024 } 1025 1026 public void onServiceDisconnected(ComponentName className) { 1027 if (DBG) Log.d(TAG, "Proxy object disconnected"); 1028 mPhoneProxy = null; 1029 } 1030 }; 1031 1032 // HFP Connection state of the device could be changed by the state machine 1033 // in separate thread while this method is executing. 1034 int getConnectionState(BluetoothDevice device) { 1035 if (getCurrentState() == mDisconnected) { 1036 return BluetoothProfile.STATE_DISCONNECTED; 1037 } 1038 1039 synchronized (this) { 1040 IState currentState = getCurrentState(); 1041 if (currentState == mPending) { 1042 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 1043 return BluetoothProfile.STATE_CONNECTING; 1044 } 1045 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 1046 return BluetoothProfile.STATE_DISCONNECTING; 1047 } 1048 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 1049 return BluetoothProfile.STATE_CONNECTING; // incoming connection 1050 } 1051 return BluetoothProfile.STATE_DISCONNECTED; 1052 } 1053 1054 if (currentState == mConnected || currentState == mAudioOn) { 1055 if (mCurrentDevice.equals(device)) { 1056 return BluetoothProfile.STATE_CONNECTED; 1057 } 1058 return BluetoothProfile.STATE_DISCONNECTED; 1059 } else { 1060 Log.e(TAG, "Bad currentState: " + currentState); 1061 return BluetoothProfile.STATE_DISCONNECTED; 1062 } 1063 } 1064 } 1065 1066 List<BluetoothDevice> getConnectedDevices() { 1067 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 1068 synchronized(this) { 1069 if (isConnected()) { 1070 devices.add(mCurrentDevice); 1071 } 1072 } 1073 return devices; 1074 } 1075 1076 boolean isAudioOn() { 1077 return (getCurrentState() == mAudioOn); 1078 } 1079 1080 boolean isAudioConnected(BluetoothDevice device) { 1081 synchronized(this) { 1082 1083 /* Additional check for audio state included for the case when PhoneApp queries 1084 Bluetooth Audio state, before we receive the close event from the stack for the 1085 sco disconnect issued in AudioOn state. This was causing a mismatch in the 1086 Incall screen UI. */ 1087 1088 if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device) 1089 && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) 1090 { 1091 return true; 1092 } 1093 } 1094 return false; 1095 } 1096 1097 int getAudioState(BluetoothDevice device) { 1098 synchronized(this) { 1099 if (mCurrentDevice == null || !mCurrentDevice.equals(device)) { 1100 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 1101 } 1102 } 1103 return mAudioState; 1104 } 1105 1106 private void processVrEvent(int state) { 1107 Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " + 1108 mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " + mWaitingForVoiceRecognition + 1109 " isInCall: " + isInCall()); 1110 if (state == HeadsetHalConstants.VR_STATE_STARTED) { 1111 if (!mVoiceRecognitionStarted && 1112 !isVirtualCallInProgress() && 1113 !isInCall()) 1114 { 1115 try { 1116 mService.startActivity(sVoiceCommandIntent); 1117 } catch (ActivityNotFoundException e) { 1118 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1119 return; 1120 } 1121 expectVoiceRecognition(); 1122 } 1123 } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) { 1124 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) 1125 { 1126 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1127 mVoiceRecognitionStarted = false; 1128 mWaitingForVoiceRecognition = false; 1129 if (!isInCall()) { 1130 disconnectAudioNative(getByteAddress(mCurrentDevice)); 1131 mAudioManager.setParameters("A2dpSuspended=false"); 1132 } 1133 } 1134 else 1135 { 1136 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1137 } 1138 } else { 1139 Log.e(TAG, "Bad Voice Recognition state: " + state); 1140 } 1141 } 1142 1143 private void processLocalVrEvent(int state) 1144 { 1145 if (state == HeadsetHalConstants.VR_STATE_STARTED) 1146 { 1147 boolean needAudio = true; 1148 if (mVoiceRecognitionStarted || isInCall()) 1149 { 1150 Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() + 1151 " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 1152 return; 1153 } 1154 mVoiceRecognitionStarted = true; 1155 1156 if (mWaitingForVoiceRecognition) 1157 { 1158 Log.d(TAG, "Voice recognition started successfully"); 1159 mWaitingForVoiceRecognition = false; 1160 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1161 removeMessages(START_VR_TIMEOUT); 1162 } 1163 else 1164 { 1165 Log.d(TAG, "Voice recognition started locally"); 1166 needAudio = startVoiceRecognitionNative(); 1167 } 1168 1169 if (needAudio && !isAudioOn()) 1170 { 1171 Log.d(TAG, "Initiating audio connection for Voice Recognition"); 1172 // At this stage, we need to be sure that AVDTP is not streaming. This is needed 1173 // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in 1174 // streaming state while a SCO connection is established. 1175 // This is needed for VoiceDial scenario alone and not for 1176 // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE 1177 // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed. 1178 // Whereas for VoiceDial we want to activate the SCO connection but we are still 1179 // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream 1180 mAudioManager.setParameters("A2dpSuspended=true"); 1181 connectAudioNative(getByteAddress(mCurrentDevice)); 1182 } 1183 1184 if (mStartVoiceRecognitionWakeLock.isHeld()) { 1185 mStartVoiceRecognitionWakeLock.release(); 1186 } 1187 } 1188 else 1189 { 1190 Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " + mVoiceRecognitionStarted + 1191 " mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition); 1192 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) 1193 { 1194 mVoiceRecognitionStarted = false; 1195 mWaitingForVoiceRecognition = false; 1196 1197 if (stopVoiceRecognitionNative() && !isInCall()) { 1198 disconnectAudioNative(getByteAddress(mCurrentDevice)); 1199 mAudioManager.setParameters("A2dpSuspended=false"); 1200 } 1201 } 1202 } 1203 } 1204 1205 private synchronized void expectVoiceRecognition() { 1206 mWaitingForVoiceRecognition = true; 1207 sendMessageDelayed(START_VR_TIMEOUT, START_VR_TIMEOUT_VALUE); 1208 if (!mStartVoiceRecognitionWakeLock.isHeld()) { 1209 mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE); 1210 } 1211 } 1212 1213 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1214 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 1215 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 1216 int connectionState; 1217 synchronized (this) { 1218 for (BluetoothDevice device : bondedDevices) { 1219 ParcelUuid[] featureUuids = device.getUuids(); 1220 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 1221 continue; 1222 } 1223 connectionState = getConnectionState(device); 1224 for(int i = 0; i < states.length; i++) { 1225 if (connectionState == states[i]) { 1226 deviceList.add(device); 1227 } 1228 } 1229 } 1230 } 1231 return deviceList; 1232 } 1233 1234 // This method does not check for error conditon (newState == prevState) 1235 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 1236 if (DBG) log("Connection state " + device + ": " + prevState + "->" + newState); 1237 if(prevState == BluetoothProfile.STATE_CONNECTED) { 1238 // Headset is disconnecting, stop Virtual call if active. 1239 terminateScoUsingVirtualVoiceCall(); 1240 } 1241 1242 /* Notifying the connection state change of the profile before sending the intent for 1243 connection state change, as it was causing a race condition, with the UI not being 1244 updated with the correct connection state. */ 1245 mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET, 1246 newState, prevState); 1247 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 1248 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1249 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1250 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1251 mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 1252 } 1253 1254 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 1255 if(prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 1256 // When SCO gets disconnected during call transfer, Virtual call 1257 //needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall. 1258 terminateScoUsingVirtualVoiceCall(); 1259 } 1260 Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 1261 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1262 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1263 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1264 mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 1265 if (DBG) log("Audio state " + device + ": " + prevState + "->" + newState); 1266 } 1267 1268 private void configAudioParameters() 1269 { 1270 // Reset NREC on connect event. Headset will override later 1271 mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName() + ";" + 1272 HEADSET_NREC + "=on"); 1273 } 1274 1275 private String parseUnknownAt(String atString) 1276 { 1277 StringBuilder atCommand = new StringBuilder(atString.length()); 1278 String result = null; 1279 1280 for (int i = 0; i < atString.length(); i++) { 1281 char c = atString.charAt(i); 1282 if (c == '"') { 1283 int j = atString.indexOf('"', i + 1 ); // search for closing " 1284 if (j == -1) { // unmatched ", insert one. 1285 atCommand.append(atString.substring(i, atString.length())); 1286 atCommand.append('"'); 1287 break; 1288 } 1289 atCommand.append(atString.substring(i, j + 1)); 1290 i = j; 1291 } else if (c != ' ') { 1292 atCommand.append(Character.toUpperCase(c)); 1293 } 1294 } 1295 result = atCommand.toString(); 1296 return result; 1297 } 1298 1299 private int getAtCommandType(String atCommand) 1300 { 1301 int commandType = mPhonebook.TYPE_UNKNOWN; 1302 String atString = null; 1303 atCommand = atCommand.trim(); 1304 if (atCommand.length() > 5) 1305 { 1306 atString = atCommand.substring(5); 1307 if (atString.startsWith("?")) // Read 1308 commandType = mPhonebook.TYPE_READ; 1309 else if (atString.startsWith("=?")) // Test 1310 commandType = mPhonebook.TYPE_TEST; 1311 else if (atString.startsWith("=")) // Set 1312 commandType = mPhonebook.TYPE_SET; 1313 else 1314 commandType = mPhonebook.TYPE_UNKNOWN; 1315 } 1316 return commandType; 1317 } 1318 1319 /* Method to check if Virtual Call in Progress */ 1320 private boolean isVirtualCallInProgress() { 1321 return mVirtualCallStarted; 1322 } 1323 1324 void setVirtualCallInProgress(boolean state) { 1325 mVirtualCallStarted = state; 1326 } 1327 1328 /* NOTE: Currently the VirtualCall API does not support handling of 1329 call transfers. If it is initiated from the handsfree device, 1330 HeadsetStateMachine will end the virtual call by calling 1331 terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */ 1332 synchronized boolean initiateScoUsingVirtualVoiceCall() { 1333 if (DBG) log("initiateScoUsingVirtualVoiceCall: Received"); 1334 // 1. Check if the SCO state is idle 1335 if (isInCall() || mVoiceRecognitionStarted) { 1336 Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress."); 1337 return false; 1338 } 1339 1340 // 2. Send virtual phone state changed to initialize SCO 1341 processCallState(new HeadsetCallState(0, 0, 1342 HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true); 1343 processCallState(new HeadsetCallState(0, 0, 1344 HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true); 1345 processCallState(new HeadsetCallState(1, 0, 1346 HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true); 1347 setVirtualCallInProgress(true); 1348 // Done 1349 if (DBG) log("initiateScoUsingVirtualVoiceCall: Done"); 1350 return true; 1351 } 1352 1353 synchronized boolean terminateScoUsingVirtualVoiceCall() { 1354 if (DBG) log("terminateScoUsingVirtualVoiceCall: Received"); 1355 1356 if (!isVirtualCallInProgress()) { 1357 Log.e(TAG, "terminateScoUsingVirtualVoiceCall:"+ 1358 "No present call to terminate"); 1359 return false; 1360 } 1361 1362 // 2. Send virtual phone state changed to close SCO 1363 processCallState(new HeadsetCallState(0, 0, 1364 HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true); 1365 setVirtualCallInProgress(false); 1366 // Done 1367 if (DBG) log("terminateScoUsingVirtualVoiceCall: Done"); 1368 return true; 1369 } 1370 1371 private void processAnswerCall() { 1372 if (mPhoneProxy != null) { 1373 try { 1374 mPhoneProxy.answerCall(); 1375 } catch (RemoteException e) { 1376 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1377 } 1378 } else { 1379 Log.e(TAG, "Handsfree phone proxy null for answering call"); 1380 } 1381 } 1382 1383 private void processHangupCall() { 1384 // Close the virtual call if active. Virtual call should be 1385 // terminated for CHUP callback event 1386 if (isVirtualCallInProgress()) { 1387 terminateScoUsingVirtualVoiceCall(); 1388 } else { 1389 if (mPhoneProxy != null) { 1390 try { 1391 mPhoneProxy.hangupCall(); 1392 } catch (RemoteException e) { 1393 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1394 } 1395 } else { 1396 Log.e(TAG, "Handsfree phone proxy null for hanging up call"); 1397 } 1398 } 1399 } 1400 1401 private void processDialCall(String number) { 1402 String dialNumber; 1403 if ((number == null) || (number.length() == 0)) { 1404 dialNumber = mPhonebook.getLastDialledNumber(); 1405 if (dialNumber == null) { 1406 if (DBG) log("processDialCall, last dial number null"); 1407 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1408 return; 1409 } 1410 } else if (number.charAt(0) == '>') { 1411 // Yuck - memory dialling requested. 1412 // Just dial last number for now 1413 if (number.startsWith(">9999")) { // for PTS test 1414 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1415 return; 1416 } 1417 if (DBG) log("processDialCall, memory dial do last dial for now"); 1418 dialNumber = mPhonebook.getLastDialledNumber(); 1419 if (dialNumber == null) { 1420 if (DBG) log("processDialCall, last dial number null"); 1421 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1422 return; 1423 } 1424 } else { 1425 // Remove trailing ';' 1426 if (number.charAt(number.length() - 1) == ';') { 1427 number = number.substring(0, number.length() - 1); 1428 } 1429 1430 dialNumber = PhoneNumberUtils.convertPreDial(number); 1431 } 1432 // Check for virtual call to terminate before sending Call Intent 1433 terminateScoUsingVirtualVoiceCall(); 1434 1435 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1436 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 1437 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1438 mService.startActivity(intent); 1439 // TODO(BT) continue send OK reults code after call starts 1440 // hold wait lock, start a timer, set wait call flag 1441 // Get call started indication from bluetooth phone 1442 mDialingOut = true; 1443 sendMessageDelayed(DIALING_OUT_TIMEOUT, DIALING_OUT_TIMEOUT_VALUE); 1444 } 1445 1446 private void processVolumeEvent(int volumeType, int volume) { 1447 if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) { 1448 mPhoneState.setSpeakerVolume(volume); 1449 int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0; 1450 mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag); 1451 } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) { 1452 mPhoneState.setMicVolume(volume); 1453 } else { 1454 Log.e(TAG, "Bad voluem type: " + volumeType); 1455 } 1456 } 1457 1458 private void processSendDtmf(int dtmf) { 1459 if (mPhoneProxy != null) { 1460 try { 1461 mPhoneProxy.sendDtmf(dtmf); 1462 } catch (RemoteException e) { 1463 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1464 } 1465 } else { 1466 Log.e(TAG, "Handsfree phone proxy null for sending DTMF"); 1467 } 1468 } 1469 1470 private void processCallState(HeadsetCallState callState) { 1471 processCallState(callState, false); 1472 } 1473 1474 private void processCallState(HeadsetCallState callState, 1475 boolean isVirtualCall) { 1476 mPhoneState.setNumActiveCall(callState.mNumActive); 1477 mPhoneState.setNumHeldCall(callState.mNumHeld); 1478 mPhoneState.setCallState(callState.mCallState); 1479 if (mDialingOut && callState.mCallState == 1480 HeadsetHalConstants.CALL_STATE_DIALING) { 1481 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1482 removeMessages(DIALING_OUT_TIMEOUT); 1483 mDialingOut = false; 1484 } 1485 log("mNumActive: " + callState.mNumActive + " mNumHeld: " + 1486 callState.mNumHeld +" mCallState: " + callState.mCallState); 1487 log("mNumber: " + callState.mNumber + " mType: " + callState.mType); 1488 if(!isVirtualCall) { 1489 /* Not a Virtual call request. End the virtual call, if running, 1490 before sending phoneStateChangeNative to BTIF */ 1491 terminateScoUsingVirtualVoiceCall(); 1492 } 1493 if (getCurrentState() != mDisconnected) { 1494 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 1495 callState.mCallState, callState.mNumber, callState.mType); 1496 } 1497 } 1498 1499 // enable 1 enable noice reduction 1500 // 0 disable noice reduction 1501 private void processNoiceReductionEvent(int enable) { 1502 if (enable == 1) { 1503 mAudioManager.setParameters(HEADSET_NREC + "=on"); 1504 } else { 1505 mAudioManager.setParameters(HEADSET_NREC + "off"); 1506 } 1507 } 1508 1509 private void processAtChld(int chld) { 1510 if (mPhoneProxy != null) { 1511 try { 1512 if (mPhoneProxy.processChld(chld)) { 1513 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1514 } else { 1515 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1516 } 1517 } catch (RemoteException e) { 1518 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1519 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1520 } 1521 } else { 1522 Log.e(TAG, "Handsfree phone proxy null for At+Chld"); 1523 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1524 } 1525 } 1526 1527 private void processSubscriberNumberRequest() { 1528 if (mPhoneProxy != null) { 1529 try { 1530 String number = mPhoneProxy.getSubscriberNumber(); 1531 if (number != null) { 1532 atResponseStringNative("+CNUM: ,\"" + number + "\"," + 1533 PhoneNumberUtils.toaFromString(number) + ",,4"); 1534 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1535 } 1536 } catch (RemoteException e) { 1537 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1538 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1539 } 1540 } else { 1541 Log.e(TAG, "Handsfree phone proxy null for At+CNUM"); 1542 } 1543 } 1544 1545 private void processAtCind() { 1546 int call, call_setup; 1547 1548 /* Handsfree carkits expect that +CIND is properly responded to 1549 Hence we ensure that a proper response is sent 1550 for the virtual call too.*/ 1551 if (isVirtualCallInProgress()) { 1552 call = 1; 1553 call_setup = 0; 1554 } else { 1555 // regular phone call 1556 call = mPhoneState.getNumActiveCall(); 1557 call_setup = mPhoneState.getNumHeldCall(); 1558 } 1559 1560 cindResponseNative(mPhoneState.getService(), call, 1561 call_setup, mPhoneState.getCallState(), 1562 mPhoneState.getSignal(), mPhoneState.getRoam(), 1563 mPhoneState.getBatteryCharge()); 1564 } 1565 1566 private void processAtCops() { 1567 if (mPhoneProxy != null) { 1568 try { 1569 String operatorName = mPhoneProxy.getNetworkOperator(); 1570 if (operatorName == null) { 1571 operatorName = ""; 1572 } 1573 copsResponseNative(operatorName); 1574 } catch (RemoteException e) { 1575 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1576 copsResponseNative(""); 1577 } 1578 } else { 1579 Log.e(TAG, "Handsfree phone proxy null for At+COPS"); 1580 copsResponseNative(""); 1581 } 1582 } 1583 1584 private void processAtClcc() { 1585 if (mPhoneProxy != null) { 1586 try { 1587 if(isVirtualCallInProgress()) { 1588 String phoneNumber = ""; 1589 int type = PhoneNumberUtils.TOA_Unknown; 1590 try { 1591 phoneNumber = mPhoneProxy.getSubscriberNumber(); 1592 type = PhoneNumberUtils.toaFromString(phoneNumber); 1593 } catch (RemoteException ee) { 1594 Log.e(TAG, "Unable to retrieve phone number"+ 1595 "using IBluetoothHeadsetPhone proxy"); 1596 phoneNumber = ""; 1597 } 1598 clccResponseNative(1, 0, 0, 0, false, phoneNumber, type); 1599 } 1600 else if (!mPhoneProxy.listCurrentCalls()) { 1601 clccResponseNative(0, 0, 0, 0, false, "", 0); 1602 } 1603 } catch (RemoteException e) { 1604 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1605 clccResponseNative(0, 0, 0, 0, false, "", 0); 1606 } 1607 } else { 1608 Log.e(TAG, "Handsfree phone proxy null for At+CLCC"); 1609 clccResponseNative(0, 0, 0, 0, false, "", 0); 1610 } 1611 } 1612 1613 private void processAtCscs(String atString, int type) { 1614 log("processAtCscs - atString = "+ atString); 1615 if(mPhonebook != null) { 1616 mPhonebook.handleCscsCommand(atString, type); 1617 } 1618 else { 1619 Log.e(TAG, "Phonebook handle null for At+CSCS"); 1620 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1621 } 1622 } 1623 1624 private void processAtCpbs(String atString, int type) { 1625 log("processAtCpbs - atString = "+ atString); 1626 if(mPhonebook != null) { 1627 mPhonebook.handleCpbsCommand(atString, type); 1628 } 1629 else { 1630 Log.e(TAG, "Phonebook handle null for At+CPBS"); 1631 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1632 } 1633 } 1634 1635 private void processAtCpbr(String atString, int type, BluetoothDevice mCurrentDevice) { 1636 log("processAtCpbr - atString = "+ atString); 1637 if(mPhonebook != null) { 1638 mPhonebook.handleCpbrCommand(atString, type, mCurrentDevice); 1639 } 1640 else { 1641 Log.e(TAG, "Phonebook handle null for At+CPBR"); 1642 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1643 } 1644 } 1645 1646 private void processUnknownAt(String atString) { 1647 // TODO (BT) 1648 log("processUnknownAt - atString = "+ atString); 1649 String atCommand = parseUnknownAt(atString); 1650 int commandType = getAtCommandType(atCommand); 1651 if (atCommand.startsWith("+CSCS")) 1652 processAtCscs(atCommand.substring(5), commandType); 1653 else if (atCommand.startsWith("+CPBS")) 1654 processAtCpbs(atCommand.substring(5), commandType); 1655 else if (atCommand.startsWith("+CPBR")) 1656 processAtCpbr(atCommand.substring(5), commandType, mCurrentDevice); 1657 else 1658 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1659 } 1660 1661 private void processKeyPressed() { 1662 if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) { 1663 if (mPhoneProxy != null) { 1664 try { 1665 mPhoneProxy.answerCall(); 1666 } catch (RemoteException e) { 1667 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1668 } 1669 } else { 1670 Log.e(TAG, "Handsfree phone proxy null for answering call"); 1671 } 1672 } else if (mPhoneState.getNumActiveCall() > 0) { 1673 if (!isAudioOn()) 1674 { 1675 connectAudioNative(getByteAddress(mCurrentDevice)); 1676 } 1677 else 1678 { 1679 if (mPhoneProxy != null) { 1680 try { 1681 mPhoneProxy.hangupCall(); 1682 } catch (RemoteException e) { 1683 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1684 } 1685 } else { 1686 Log.e(TAG, "Handsfree phone proxy null for hangup call"); 1687 } 1688 } 1689 } else { 1690 String dialNumber = mPhonebook.getLastDialledNumber(); 1691 if (dialNumber == null) { 1692 if (DBG) log("processKeyPressed, last dial number null"); 1693 return; 1694 } 1695 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1696 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 1697 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1698 mService.startActivity(intent); 1699 } 1700 } 1701 1702 private void onConnectionStateChanged(int state, byte[] address) { 1703 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 1704 event.valueInt = state; 1705 event.device = getDevice(address); 1706 sendMessage(STACK_EVENT, event); 1707 } 1708 1709 private void onAudioStateChanged(int state, byte[] address) { 1710 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 1711 event.valueInt = state; 1712 event.device = getDevice(address); 1713 sendMessage(STACK_EVENT, event); 1714 } 1715 1716 private void onVrStateChanged(int state) { 1717 StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED); 1718 event.valueInt = state; 1719 sendMessage(STACK_EVENT, event); 1720 } 1721 1722 private void onAnswerCall() { 1723 StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL); 1724 sendMessage(STACK_EVENT, event); 1725 } 1726 1727 private void onHangupCall() { 1728 StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL); 1729 sendMessage(STACK_EVENT, event); 1730 } 1731 1732 private void onVolumeChanged(int type, int volume) { 1733 StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED); 1734 event.valueInt = type; 1735 event.valueInt2 = volume; 1736 sendMessage(STACK_EVENT, event); 1737 } 1738 1739 private void onDialCall(String number) { 1740 StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL); 1741 event.valueString = number; 1742 sendMessage(STACK_EVENT, event); 1743 } 1744 1745 private void onSendDtmf(int dtmf) { 1746 StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF); 1747 event.valueInt = dtmf; 1748 sendMessage(STACK_EVENT, event); 1749 } 1750 1751 private void onNoiceReductionEnable(boolean enable) { 1752 StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION); 1753 event.valueInt = enable ? 1 : 0; 1754 sendMessage(STACK_EVENT, event); 1755 } 1756 1757 private void onAtChld(int chld) { 1758 StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD); 1759 event.valueInt = chld; 1760 sendMessage(STACK_EVENT, event); 1761 } 1762 1763 private void onAtCnum() { 1764 StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST); 1765 sendMessage(STACK_EVENT, event); 1766 } 1767 1768 private void onAtCind() { 1769 StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND); 1770 sendMessage(STACK_EVENT, event); 1771 } 1772 1773 private void onAtCops() { 1774 StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS); 1775 sendMessage(STACK_EVENT, event); 1776 } 1777 1778 private void onAtClcc() { 1779 StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC); 1780 sendMessage(STACK_EVENT, event); 1781 } 1782 1783 private void onUnknownAt(String atString) { 1784 StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT); 1785 event.valueString = atString; 1786 sendMessage(STACK_EVENT, event); 1787 } 1788 1789 private void onKeyPressed() { 1790 StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED); 1791 sendMessage(STACK_EVENT, event); 1792 } 1793 1794 private void processIntentBatteryChanged(Intent intent) { 1795 int batteryLevel = intent.getIntExtra("level", -1); 1796 int scale = intent.getIntExtra("scale", -1); 1797 if (batteryLevel == -1 || scale == -1 || scale == 0) { 1798 Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale); 1799 return; 1800 } 1801 batteryLevel = batteryLevel * 5 / scale; 1802 mPhoneState.setBatteryCharge(batteryLevel); 1803 } 1804 1805 private void processRoamChanged(boolean roam) { 1806 mPhoneState.setRoam(roam ? HeadsetHalConstants.SERVICE_TYPE_ROAMING : 1807 HeadsetHalConstants.SERVICE_TYPE_HOME); 1808 } 1809 1810 private void processDeviceStateChanged(HeadsetDeviceState deviceState) { 1811 notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal, 1812 deviceState.mBatteryCharge); 1813 } 1814 1815 private void processSendClccResponse(HeadsetClccResponse clcc) { 1816 clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty, 1817 clcc.mNumber, clcc.mType); 1818 } 1819 1820 private String getCurrentDeviceName() { 1821 String defaultName = "<unknown>"; 1822 if (mCurrentDevice == null) { 1823 return defaultName; 1824 } 1825 String deviceName = mCurrentDevice.getName(); 1826 if (deviceName == null) { 1827 return defaultName; 1828 } 1829 return deviceName; 1830 } 1831 1832 private byte[] getByteAddress(BluetoothDevice device) { 1833 return Utils.getBytesFromAddress(device.getAddress()); 1834 } 1835 1836 private BluetoothDevice getDevice(byte[] address) { 1837 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 1838 } 1839 1840 private boolean isInCall() { 1841 return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) || 1842 (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE)); 1843 } 1844 1845 boolean isConnected() { 1846 IState currentState = getCurrentState(); 1847 return (currentState == mConnected || currentState == mAudioOn); 1848 } 1849 1850 private void log(String msg) { 1851 if (DBG) { 1852 Log.d(TAG, msg); 1853 } 1854 } 1855 1856 public void handleAccessPermissionResult(Intent intent) { 1857 log("handleAccessPermissionResult"); 1858 if(mPhonebook != null) { 1859 if (!mPhonebook.getCheckingAccessPermission()) { 1860 return; 1861 } 1862 int atCommandResult = 0; 1863 int atCommandErrorCode = 0; 1864 //HeadsetBase headset = mHandsfree.getHeadset(); 1865 // ASSERT: (headset != null) && headSet.isConnected() 1866 // REASON: mCheckingAccessPermission is true, otherwise resetAtState 1867 // has set mCheckingAccessPermission to false 1868 if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 1869 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 1870 BluetoothDevice.CONNECTION_ACCESS_NO) == 1871 BluetoothDevice.CONNECTION_ACCESS_YES) { 1872 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 1873 mCurrentDevice.setTrust(true); 1874 } 1875 atCommandResult = mPhonebook.processCpbrCommand(); 1876 } 1877 } 1878 mPhonebook.setCpbrIndex(-1); 1879 mPhonebook.setCheckingAccessPermission(false); 1880 1881 if (atCommandResult >= 0) { 1882 atResponseCodeNative(atCommandResult, atCommandErrorCode); 1883 } 1884 else 1885 log("handleAccessPermissionResult - RESULT_NONE"); 1886 } 1887 else { 1888 Log.e(TAG, "Phonebook handle null"); 1889 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1890 } 1891 } 1892 1893 private static final String SCHEME_TEL = "tel"; 1894 1895 // Event types for STACK_EVENT message 1896 final private static int EVENT_TYPE_NONE = 0; 1897 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 1898 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 1899 final private static int EVENT_TYPE_VR_STATE_CHANGED = 3; 1900 final private static int EVENT_TYPE_ANSWER_CALL = 4; 1901 final private static int EVENT_TYPE_HANGUP_CALL = 5; 1902 final private static int EVENT_TYPE_VOLUME_CHANGED = 6; 1903 final private static int EVENT_TYPE_DIAL_CALL = 7; 1904 final private static int EVENT_TYPE_SEND_DTMF = 8; 1905 final private static int EVENT_TYPE_NOICE_REDUCTION = 9; 1906 final private static int EVENT_TYPE_AT_CHLD = 10; 1907 final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11; 1908 final private static int EVENT_TYPE_AT_CIND = 12; 1909 final private static int EVENT_TYPE_AT_COPS = 13; 1910 final private static int EVENT_TYPE_AT_CLCC = 14; 1911 final private static int EVENT_TYPE_UNKNOWN_AT = 15; 1912 final private static int EVENT_TYPE_KEY_PRESSED = 16; 1913 1914 private class StackEvent { 1915 int type = EVENT_TYPE_NONE; 1916 int valueInt = 0; 1917 int valueInt2 = 0; 1918 String valueString = null; 1919 BluetoothDevice device = null; 1920 1921 private StackEvent(int type) { 1922 this.type = type; 1923 } 1924 } 1925 1926 /*package*/native boolean atResponseCodeNative(int responseCode, int errorCode); 1927 /*package*/ native boolean atResponseStringNative(String responseString); 1928 1929 private native static void classInitNative(); 1930 private native void initializeNative(); 1931 private native void cleanupNative(); 1932 private native boolean connectHfpNative(byte[] address); 1933 private native boolean disconnectHfpNative(byte[] address); 1934 private native boolean connectAudioNative(byte[] address); 1935 private native boolean disconnectAudioNative(byte[] address); 1936 private native boolean startVoiceRecognitionNative(); 1937 private native boolean stopVoiceRecognitionNative(); 1938 private native boolean setVolumeNative(int volumeType, int volume); 1939 private native boolean cindResponseNative(int service, int numActive, int numHeld, 1940 int callState, int signal, int roam, 1941 int batteryCharge); 1942 private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal, 1943 int batteryCharge); 1944 1945 private native boolean clccResponseNative(int index, int dir, int status, int mode, 1946 boolean mpty, String number, int type); 1947 private native boolean copsResponseNative(String operatorName); 1948 1949 private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState, 1950 String number, int type); 1951} 1952