HeadsetStateMachine.java revision 6654f5c903de510a70f9e72cd5ad7837b615d93f
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.internal.util.IState; 48import com.android.internal.util.State; 49import com.android.internal.util.StateMachine; 50import java.util.ArrayList; 51import java.util.List; 52import java.util.Set; 53 54final class HeadsetStateMachine extends StateMachine { 55 private static final String TAG = "HeadsetStateMachine"; 56 private static final boolean DBG = true; 57 58 static final int CONNECT = 1; 59 static final int DISCONNECT = 2; 60 static final int CONNECT_AUDIO = 3; 61 static final int DISCONNECT_AUDIO = 4; 62 static final int VOICE_RECOGNITION_START = 5; 63 static final int VOICE_RECOGNITION_STOP = 6; 64 65 // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION 66 // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO 67 static final int INTENT_SCO_VOLUME_CHANGED = 7; 68 static final int SET_MIC_VOLUME = 8; 69 static final int CALL_STATE_CHANGED = 9; 70 static final int INTENT_BATTERY_CHANGED = 10; 71 static final int DEVICE_STATE_CHANGED = 11; 72 static final int ROAM_CHANGED = 12; 73 static final int SEND_CCLC_RESPONSE = 13; 74 75 private static final int STACK_EVENT = 101; 76 private static final int DIALING_OUT_TIMEOUT = 102; 77 private static final int START_VR_TIMEOUT = 103; 78 79 private static final int CONNECT_TIMEOUT = 201; 80 81 private static final int DIALING_OUT_TIMEOUT_VALUE = 10000; 82 private static final int START_VR_TIMEOUT_VALUE = 5000; 83 84 private static final ParcelUuid[] HEADSET_UUIDS = { 85 BluetoothUuid.HSP, 86 BluetoothUuid.Handsfree, 87 }; 88 89 private Disconnected mDisconnected; 90 private Pending mPending; 91 private Connected mConnected; 92 private AudioOn mAudioOn; 93 94 private Context mContext; 95 96 private PowerManager mPowerManager; 97 98 private boolean mVoiceRecognitionStarted = false; 99 private boolean mWaitingForVoiceRecognition = false; 100 private WakeLock mStartVoiceRecognitionWakeLock; // held while waiting for voice recognition 101 102 private boolean mDialingOut = false; 103 private AudioManager mAudioManager; 104 private AtPhonebook mPhonebook; 105 106 private static Intent sVoiceCommandIntent; 107 108 private HeadsetPhoneState mPhoneState; 109 private int mAudioState; 110 private BluetoothAdapter mAdapter; 111 private IBluetooth mAdapterService; 112 private IBluetoothHeadsetPhone mPhoneProxy; 113 114 // mCurrentDevice is the device connected before the state changes 115 // mTargetDevice is the device to be connected 116 // mIncomingDevice is the device connecting to us, valid only in Pending state 117 // when mIncomingDevice is not null, both mCurrentDevice 118 // and mTargetDevice are null 119 // when either mCurrentDevice or mTargetDevice is not null, 120 // mIncomingDevice is null 121 // Stable states 122 // No connection, Disconnected state 123 // both mCurrentDevice and mTargetDevice are null 124 // Connected, Connected state 125 // mCurrentDevice is not null, mTargetDevice is null 126 // Interim states 127 // Connecting to a device, Pending 128 // mCurrentDevice is null, mTargetDevice is not null 129 // Disconnecting device, Connecting to new device 130 // Pending 131 // Both mCurrentDevice and mTargetDevice are not null 132 // Disconnecting device Pending 133 // mCurrentDevice is not null, mTargetDevice is null 134 // Incoming connections Pending 135 // Both mCurrentDevice and mTargetDevice are null 136 private BluetoothDevice mCurrentDevice = null; 137 private BluetoothDevice mTargetDevice = null; 138 private BluetoothDevice mIncomingDevice = null; 139 140 static { 141 classInitNative(); 142 } 143 144 HeadsetStateMachine(Context context) { 145 super(TAG); 146 147 mContext = context; 148 mVoiceRecognitionStarted = false; 149 mWaitingForVoiceRecognition = false; 150 151 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 152 mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 153 TAG + ":VoiceRecognition"); 154 mStartVoiceRecognitionWakeLock.setReferenceCounted(false); 155 156 mDialingOut = false; 157 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 158 mPhonebook = new AtPhonebook(mContext); 159 mPhoneState = new HeadsetPhoneState(context, this); 160 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 161 mAdapter = BluetoothAdapter.getDefaultAdapter(); 162 mAdapterService = IBluetooth.Stub.asInterface(ServiceManager.getService("bluetooth")); 163 if (!context.bindService(new Intent(IBluetoothHeadsetPhone.class.getName()), 164 mConnection, 0)) { 165 Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service"); 166 } 167 168 initializeNative(); 169 170 mDisconnected = new Disconnected(); 171 mPending = new Pending(); 172 mConnected = new Connected(); 173 mAudioOn = new AudioOn(); 174 175 if (sVoiceCommandIntent == null) { 176 sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND); 177 sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 178 } 179 180 addState(mDisconnected); 181 addState(mPending); 182 addState(mConnected); 183 addState(mAudioOn); 184 185 setInitialState(mDisconnected); 186 } 187 188 public void cleanup() { 189 cleanupNative(); 190 if (mPhoneProxy != null) { 191 if (DBG) Log.d(TAG,"Unbinding service..."); 192 synchronized (mConnection) { 193 try { 194 mPhoneProxy = null; 195 mContext.unbindService(mConnection); 196 } catch (Exception re) { 197 Log.e(TAG,"",re); 198 } 199 } 200 } 201 } 202 203 private class Disconnected extends State { 204 @Override 205 public void enter() { 206 log("Enter Disconnected: " + getCurrentMessage().what); 207 mPhoneState.listenForPhoneState(false); 208 } 209 210 @Override 211 public boolean processMessage(Message message) { 212 log("Disconnected process message: " + message.what); 213 if (DBG) { 214 if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) { 215 log("ERROR: current, target, or mIncomingDevice not null in Disconnected"); 216 return NOT_HANDLED; 217 } 218 } 219 220 boolean retValue = HANDLED; 221 switch(message.what) { 222 case CONNECT: 223 BluetoothDevice device = (BluetoothDevice) message.obj; 224 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 225 BluetoothProfile.STATE_DISCONNECTED); 226 227 if (!connectHfpNative(getByteAddress(device)) ) { 228 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 229 BluetoothProfile.STATE_CONNECTING); 230 break; 231 } 232 233 synchronized (HeadsetStateMachine.this) { 234 mTargetDevice = device; 235 transitionTo(mPending); 236 } 237 // TODO(BT) remove CONNECT_TIMEOUT when the stack 238 // sends back events consistently 239 sendMessageDelayed(CONNECT_TIMEOUT, 30000); 240 break; 241 case DISCONNECT: 242 // ignore 243 break; 244 case INTENT_BATTERY_CHANGED: 245 processIntentBatteryChanged((Intent) message.obj); 246 break; 247 case ROAM_CHANGED: 248 processRoamChanged((Boolean) message.obj); 249 break; 250 case CALL_STATE_CHANGED: 251 processCallState((HeadsetCallState) message.obj); 252 break; 253 case STACK_EVENT: 254 StackEvent event = (StackEvent) message.obj; 255 if (DBG) { 256 log("event type: " + event.type); 257 } 258 switch (event.type) { 259 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 260 processConnectionEvent(event.valueInt, event.device); 261 break; 262 default: 263 Log.e(TAG, "Unexpected stack event: " + event.type); 264 break; 265 } 266 break; 267 default: 268 return NOT_HANDLED; 269 } 270 return retValue; 271 } 272 273 @Override 274 public void exit() { 275 log("Exit Disconnected: " + getCurrentMessage().what); 276 mPhoneState.listenForPhoneState(true); 277 } 278 279 // in Disconnected state 280 private void processConnectionEvent(int state, BluetoothDevice device) { 281 switch (state) { 282 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 283 Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device); 284 break; 285 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 286 // TODO(BT) Assume it's incoming connection 287 // Do we need to check priority and accept/reject accordingly? 288 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 289 BluetoothProfile.STATE_DISCONNECTED); 290 synchronized (HeadsetStateMachine.this) { 291 mIncomingDevice = device; 292 transitionTo(mPending); 293 } 294 break; 295 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 296 Log.w(TAG, "HFP Connected from Disconnected state"); 297 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 298 BluetoothProfile.STATE_DISCONNECTED); 299 synchronized (HeadsetStateMachine.this) { 300 mCurrentDevice = device; 301 transitionTo(mConnected); 302 } 303 break; 304 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 305 Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device); 306 break; 307 default: 308 Log.e(TAG, "Incorrect state: " + state); 309 break; 310 } 311 } 312 } 313 314 private class Pending extends State { 315 @Override 316 public void enter() { 317 log("Enter Pending: " + getCurrentMessage().what); 318 } 319 320 @Override 321 public boolean processMessage(Message message) { 322 log("Pending process message: " + message.what); 323 324 boolean retValue = HANDLED; 325 switch(message.what) { 326 case CONNECT: 327 case CONNECT_AUDIO: 328 deferMessage(message); 329 break; 330 case CONNECT_TIMEOUT: 331 onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, 332 getByteAddress(mTargetDevice)); 333 break; 334 case DISCONNECT: 335 BluetoothDevice device = (BluetoothDevice) message.obj; 336 if (mCurrentDevice != null && mTargetDevice != null && 337 mTargetDevice.equals(device) ) { 338 // cancel connection to the mTargetDevice 339 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 340 BluetoothProfile.STATE_CONNECTING); 341 synchronized (HeadsetStateMachine.this) { 342 mTargetDevice = null; 343 } 344 } else { 345 deferMessage(message); 346 } 347 break; 348 case INTENT_BATTERY_CHANGED: 349 processIntentBatteryChanged((Intent) message.obj); 350 break; 351 case ROAM_CHANGED: 352 processRoamChanged((Boolean) message.obj); 353 break; 354 case CALL_STATE_CHANGED: 355 processCallState((HeadsetCallState) message.obj); 356 break; 357 case STACK_EVENT: 358 StackEvent event = (StackEvent) message.obj; 359 if (DBG) { 360 log("event type: " + event.type); 361 } 362 switch (event.type) { 363 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 364 removeMessages(CONNECT_TIMEOUT); 365 processConnectionEvent(event.valueInt, event.device); 366 break; 367 default: 368 Log.e(TAG, "Unexpected event: " + event.type); 369 break; 370 } 371 break; 372 default: 373 return NOT_HANDLED; 374 } 375 return retValue; 376 } 377 378 // in Pending state 379 private void processConnectionEvent(int state, BluetoothDevice device) { 380 switch (state) { 381 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 382 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 383 broadcastConnectionState(mCurrentDevice, 384 BluetoothProfile.STATE_DISCONNECTED, 385 BluetoothProfile.STATE_DISCONNECTING); 386 synchronized (HeadsetStateMachine.this) { 387 mCurrentDevice = null; 388 } 389 390 if (mTargetDevice != null) { 391 if (!connectHfpNative(getByteAddress(mTargetDevice))) { 392 broadcastConnectionState(mTargetDevice, 393 BluetoothProfile.STATE_DISCONNECTED, 394 BluetoothProfile.STATE_CONNECTING); 395 synchronized (HeadsetStateMachine.this) { 396 mTargetDevice = null; 397 transitionTo(mDisconnected); 398 } 399 } 400 } else { 401 synchronized (HeadsetStateMachine.this) { 402 mIncomingDevice = null; 403 transitionTo(mDisconnected); 404 } 405 } 406 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 407 // outgoing connection failed 408 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 409 BluetoothProfile.STATE_CONNECTING); 410 synchronized (HeadsetStateMachine.this) { 411 mTargetDevice = null; 412 transitionTo(mDisconnected); 413 } 414 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 415 broadcastConnectionState(mIncomingDevice, 416 BluetoothProfile.STATE_DISCONNECTED, 417 BluetoothProfile.STATE_CONNECTING); 418 synchronized (HeadsetStateMachine.this) { 419 mIncomingDevice = null; 420 transitionTo(mDisconnected); 421 } 422 } else { 423 Log.e(TAG, "Unknown device Disconnected: " + device); 424 } 425 break; 426 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 427 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 428 // disconnection failed 429 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 430 BluetoothProfile.STATE_DISCONNECTING); 431 if (mTargetDevice != null) { 432 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 433 BluetoothProfile.STATE_CONNECTING); 434 } 435 synchronized (HeadsetStateMachine.this) { 436 mTargetDevice = null; 437 transitionTo(mConnected); 438 } 439 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 440 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 441 BluetoothProfile.STATE_CONNECTING); 442 synchronized (HeadsetStateMachine.this) { 443 mCurrentDevice = mTargetDevice; 444 mTargetDevice = null; 445 transitionTo(mConnected); 446 } 447 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 448 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 449 BluetoothProfile.STATE_CONNECTING); 450 synchronized (HeadsetStateMachine.this) { 451 mCurrentDevice = mIncomingDevice; 452 mIncomingDevice = null; 453 transitionTo(mConnected); 454 } 455 } else { 456 Log.e(TAG, "Unknown device Connected: " + device); 457 // something is wrong here, but sync our state with stack 458 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 459 BluetoothProfile.STATE_DISCONNECTED); 460 synchronized (HeadsetStateMachine.this) { 461 mCurrentDevice = device; 462 mTargetDevice = null; 463 mIncomingDevice = null; 464 transitionTo(mConnected); 465 } 466 } 467 break; 468 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 469 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 470 log("current device tries to connect back"); 471 // TODO(BT) ignore or reject 472 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 473 // The stack is connecting to target device or 474 // there is an incoming connection from the target device at the same time 475 // we already broadcasted the intent, doing nothing here 476 if (DBG) { 477 log("Stack and target device are connecting"); 478 } 479 } 480 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 481 Log.e(TAG, "Another connecting event on the incoming device"); 482 } else { 483 // We get an incoming connecting request while Pending 484 // TODO(BT) is stack handing this case? let's ignore it for now 485 log("Incoming connection while pending, ignore"); 486 } 487 break; 488 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 489 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 490 // we already broadcasted the intent, doing nothing here 491 if (DBG) { 492 log("stack is disconnecting mCurrentDevice"); 493 } 494 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 495 Log.e(TAG, "TargetDevice is getting disconnected"); 496 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 497 Log.e(TAG, "IncomingDevice is getting disconnected"); 498 } else { 499 Log.e(TAG, "Disconnecting unknow device: " + device); 500 } 501 break; 502 default: 503 Log.e(TAG, "Incorrect state: " + state); 504 break; 505 } 506 } 507 508 } 509 510 private class Connected extends State { 511 @Override 512 public void enter() { 513 log("Enter Connected: " + getCurrentMessage().what); 514 } 515 516 @Override 517 public boolean processMessage(Message message) { 518 log("Connected process message: " + message.what); 519 if (DBG) { 520 if (mCurrentDevice == null) { 521 log("ERROR: mCurrentDevice is null in Connected"); 522 return NOT_HANDLED; 523 } 524 } 525 526 boolean retValue = HANDLED; 527 switch(message.what) { 528 case CONNECT: 529 { 530 BluetoothDevice device = (BluetoothDevice) message.obj; 531 if (mCurrentDevice.equals(device)) { 532 break; 533 } 534 535 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 536 BluetoothProfile.STATE_DISCONNECTED); 537 if (!disconnectHfpNative(getByteAddress(mCurrentDevice))) { 538 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 539 BluetoothProfile.STATE_CONNECTING); 540 break; 541 } 542 543 synchronized (HeadsetStateMachine.this) { 544 mTargetDevice = device; 545 transitionTo(mPending); 546 } 547 } 548 break; 549 case DISCONNECT: 550 { 551 BluetoothDevice device = (BluetoothDevice) message.obj; 552 if (!mCurrentDevice.equals(device)) { 553 break; 554 } 555 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 556 BluetoothProfile.STATE_CONNECTED); 557 if (!disconnectHfpNative(getByteAddress(device))) { 558 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 559 BluetoothProfile.STATE_DISCONNECTED); 560 break; 561 } 562 transitionTo(mPending); 563 } 564 break; 565 case CONNECT_AUDIO: 566 // TODO(BT) when failure, broadcast audio connecting to disconnected intent 567 // check if device matches mCurrentDevice 568 connectAudioNative(getByteAddress(mCurrentDevice)); 569 break; 570 case VOICE_RECOGNITION_START: 571 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED); 572 break; 573 case VOICE_RECOGNITION_STOP: 574 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); 575 break; 576 case CALL_STATE_CHANGED: 577 processCallState((HeadsetCallState) message.obj); 578 break; 579 case INTENT_BATTERY_CHANGED: 580 processIntentBatteryChanged((Intent) message.obj); 581 break; 582 case ROAM_CHANGED: 583 processRoamChanged((Boolean) message.obj); 584 break; 585 case DEVICE_STATE_CHANGED: 586 processDeviceStateChanged((HeadsetDeviceState) message.obj); 587 break; 588 case SEND_CCLC_RESPONSE: 589 processSendClccResponse((HeadsetClccResponse) message.obj); 590 break; 591 case DIALING_OUT_TIMEOUT: 592 if (mDialingOut) { 593 mDialingOut= false; 594 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 595 } 596 break; 597 case START_VR_TIMEOUT: 598 if (mWaitingForVoiceRecognition) { 599 mWaitingForVoiceRecognition = false; 600 Log.e(TAG, "Timeout waiting for voice recognition to start"); 601 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 602 } 603 break; 604 case STACK_EVENT: 605 StackEvent event = (StackEvent) message.obj; 606 if (DBG) { 607 log("event type: " + event.type); 608 } 609 switch (event.type) { 610 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 611 processConnectionEvent(event.valueInt, event.device); 612 break; 613 case EVENT_TYPE_AUDIO_STATE_CHANGED: 614 processAudioEvent(event.valueInt, event.device); 615 break; 616 case EVENT_TYPE_VR_STATE_CHANGED: 617 processVrEvent(event.valueInt); 618 break; 619 case EVENT_TYPE_ANSWER_CALL: 620 // TODO(BT) could answer call happen on Connected state? 621 processAnswerCall(); 622 break; 623 case EVENT_TYPE_HANGUP_CALL: 624 // TODO(BT) could hangup call happen on Connected state? 625 processHangupCall(); 626 break; 627 case EVENT_TYPE_VOLUME_CHANGED: 628 processVolumeEvent(event.valueInt, event.valueInt2); 629 break; 630 case EVENT_TYPE_DIAL_CALL: 631 processDialCall(event.valueString); 632 break; 633 case EVENT_TYPE_SEND_DTMF: 634 processSendDtmf(event.valueInt); 635 break; 636 case EVENT_TYPE_AT_CHLD: 637 processAtChld(event.valueInt); 638 break; 639 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 640 processSubscriberNumberRequest(); 641 break; 642 case EVENT_TYPE_AT_CIND: 643 processAtCind(); 644 break; 645 case EVENT_TYPE_AT_COPS: 646 processAtCops(); 647 break; 648 case EVENT_TYPE_AT_CLCC: 649 processAtClcc(); 650 break; 651 case EVENT_TYPE_UNKNOWN_AT: 652 processUnknownAt(event.valueString); 653 break; 654 case EVENT_TYPE_KEY_PRESSED: 655 processKeyPressed(); 656 break; 657 default: 658 Log.e(TAG, "Unknown stack event: " + event.type); 659 break; 660 } 661 break; 662 default: 663 return NOT_HANDLED; 664 } 665 return retValue; 666 } 667 668 // in Connected state 669 private void processConnectionEvent(int state, BluetoothDevice device) { 670 switch (state) { 671 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 672 if (mCurrentDevice.equals(device)) { 673 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 674 BluetoothProfile.STATE_CONNECTED); 675 synchronized (HeadsetStateMachine.this) { 676 mCurrentDevice = null; 677 transitionTo(mDisconnected); 678 } 679 } else { 680 Log.e(TAG, "Disconnected from unknown device: " + device); 681 } 682 break; 683 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 684 processSlcConnected(); 685 break; 686 default: 687 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 688 break; 689 } 690 } 691 692 // in Connected state 693 private void processAudioEvent(int state, BluetoothDevice device) { 694 if (!mCurrentDevice.equals(device)) { 695 Log.e(TAG, "Audio changed on disconnected device: " + device); 696 return; 697 } 698 699 switch (state) { 700 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 701 // TODO(BT) should I save the state for next broadcast as the prevState? 702 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED; 703 mAudioManager.setBluetoothScoOn(true); 704 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED, 705 BluetoothHeadset.STATE_AUDIO_CONNECTING); 706 transitionTo(mAudioOn); 707 break; 708 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 709 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING; 710 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING, 711 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 712 break; 713 // TODO(BT) process other states 714 default: 715 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 716 break; 717 } 718 } 719 720 private void processSlcConnected() { 721 if (mPhoneProxy != null) { 722 try { 723 mPhoneProxy.queryPhoneState(); 724 } catch (RemoteException e) { 725 Log.e(TAG, Log.getStackTraceString(new Throwable())); 726 } 727 } else { 728 Log.e(TAG, "Handsfree phone proxy null for query phone state"); 729 } 730 731 } 732 } 733 734 private class AudioOn extends State { 735 // Audio parameters 736 private static final String HEADSET_NAME = "bt_headset_name"; 737 private static final String HEADSET_NREC = "bt_headset_nrec"; 738 739 @Override 740 public void enter() { 741 log("Enter AudioOn: " + getCurrentMessage().what); 742 mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName() + ";" + 743 HEADSET_NREC + "=on"); 744 } 745 746 @Override 747 public boolean processMessage(Message message) { 748 log("AudioOn process message: " + message.what); 749 if (DBG) { 750 if (mCurrentDevice == null) { 751 log("ERROR: mCurrentDevice is null in AudioOn"); 752 return NOT_HANDLED; 753 } 754 } 755 756 boolean retValue = HANDLED; 757 switch(message.what) { 758 case DISCONNECT: 759 { 760 BluetoothDevice device = (BluetoothDevice) message.obj; 761 if (!mCurrentDevice.equals(device)) { 762 break; 763 } 764 deferMessage(obtainMessage(DISCONNECT, message.obj)); 765 } 766 // fall through 767 case DISCONNECT_AUDIO: 768 disconnectAudioNative(getByteAddress(mCurrentDevice)); 769 break; 770 case VOICE_RECOGNITION_START: 771 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED); 772 break; 773 case VOICE_RECOGNITION_STOP: 774 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); 775 break; 776 case INTENT_SCO_VOLUME_CHANGED: 777 processIntentScoVolume((Intent) message.obj); 778 break; 779 case CALL_STATE_CHANGED: 780 processCallState((HeadsetCallState) message.obj); 781 break; 782 case INTENT_BATTERY_CHANGED: 783 processIntentBatteryChanged((Intent) message.obj); 784 break; 785 case ROAM_CHANGED: 786 processRoamChanged((Boolean) message.obj); 787 break; 788 case DEVICE_STATE_CHANGED: 789 processDeviceStateChanged((HeadsetDeviceState) message.obj); 790 break; 791 case SEND_CCLC_RESPONSE: 792 processSendClccResponse((HeadsetClccResponse) message.obj); 793 break; 794 case DIALING_OUT_TIMEOUT: 795 if (mDialingOut) { 796 mDialingOut= false; 797 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 798 } 799 break; 800 case START_VR_TIMEOUT: 801 if (mWaitingForVoiceRecognition) { 802 mWaitingForVoiceRecognition = false; 803 Log.e(TAG, "Timeout waiting for voice recognition to start"); 804 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 805 } 806 break; 807 case STACK_EVENT: 808 StackEvent event = (StackEvent) message.obj; 809 if (DBG) { 810 log("event type: " + event.type); 811 } 812 switch (event.type) { 813 case EVENT_TYPE_AUDIO_STATE_CHANGED: 814 processAudioEvent(event.valueInt, event.device); 815 break; 816 case EVENT_TYPE_VR_STATE_CHANGED: 817 processVrEvent(event.valueInt); 818 break; 819 case EVENT_TYPE_ANSWER_CALL: 820 processAnswerCall(); 821 break; 822 case EVENT_TYPE_HANGUP_CALL: 823 processHangupCall(); 824 break; 825 case EVENT_TYPE_VOLUME_CHANGED: 826 processVolumeEvent(event.valueInt, event.valueInt2); 827 break; 828 case EVENT_TYPE_DIAL_CALL: 829 processDialCall(event.valueString); 830 break; 831 case EVENT_TYPE_SEND_DTMF: 832 processSendDtmf(event.valueInt); 833 break; 834 case EVENT_TYPE_NOICE_REDUCTION: 835 processNoiceReductionEvent(event.valueInt); 836 break; 837 case EVENT_TYPE_AT_CHLD: 838 processAtChld(event.valueInt); 839 break; 840 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 841 processSubscriberNumberRequest(); 842 break; 843 case EVENT_TYPE_AT_CIND: 844 processAtCind(); 845 break; 846 case EVENT_TYPE_AT_COPS: 847 processAtCops(); 848 break; 849 case EVENT_TYPE_AT_CLCC: 850 processAtClcc(); 851 break; 852 case EVENT_TYPE_UNKNOWN_AT: 853 processUnknownAt(event.valueString); 854 break; 855 case EVENT_TYPE_KEY_PRESSED: 856 processKeyPressed(); 857 break; 858 default: 859 Log.e(TAG, "Unknown stack event: " + event.type); 860 break; 861 } 862 break; 863 default: 864 return NOT_HANDLED; 865 } 866 return retValue; 867 } 868 869 // in AudioOn state 870 private void processAudioEvent(int state, BluetoothDevice device) { 871 if (!mCurrentDevice.equals(device)) { 872 Log.e(TAG, "Audio changed on disconnected device: " + device); 873 return; 874 } 875 876 switch (state) { 877 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 878 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 879 mAudioManager.setBluetoothScoOn(false); 880 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 881 BluetoothHeadset.STATE_AUDIO_CONNECTED); 882 transitionTo(mConnected); 883 break; 884 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 885 // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset? 886 //broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING, 887 // BluetoothHeadset.STATE_AUDIO_CONNECTED); 888 break; 889 default: 890 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 891 break; 892 } 893 } 894 895 // enable 1 enable noice reduction 896 // 0 disable noice reduction 897 private void processNoiceReductionEvent(int enable) { 898 if (enable == 1) { 899 mAudioManager.setParameters(HEADSET_NREC + "=on"); 900 } else { 901 mAudioManager.setParameters(HEADSET_NREC + "off"); 902 } 903 } 904 905 private void processIntentScoVolume(Intent intent) { 906 int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 907 if (mPhoneState.getSpeakerVolume() != volumeValue) { 908 mPhoneState.setSpeakerVolume(volumeValue); 909 setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue); 910 } 911 } 912 } 913 914 private ServiceConnection mConnection = new ServiceConnection() { 915 public void onServiceConnected(ComponentName className, IBinder service) { 916 if (DBG) Log.d(TAG, "Proxy object connected"); 917 mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service); 918 } 919 920 public void onServiceDisconnected(ComponentName className) { 921 if (DBG) Log.d(TAG, "Proxy object disconnected"); 922 mPhoneProxy = null; 923 } 924 }; 925 926 // HFP Connection state of the device could be changed by the state machine 927 // in separate thread while this method is executing. 928 int getConnectionState(BluetoothDevice device) { 929 if (getCurrentState() == mDisconnected) { 930 return BluetoothProfile.STATE_DISCONNECTED; 931 } 932 933 synchronized (this) { 934 IState currentState = getCurrentState(); 935 if (currentState == mPending) { 936 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 937 return BluetoothProfile.STATE_CONNECTING; 938 } 939 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 940 return BluetoothProfile.STATE_DISCONNECTING; 941 } 942 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 943 return BluetoothProfile.STATE_CONNECTING; // incoming connection 944 } 945 return BluetoothProfile.STATE_DISCONNECTED; 946 } 947 948 if (currentState == mConnected || currentState == mAudioOn) { 949 if (mCurrentDevice.equals(device)) { 950 return BluetoothProfile.STATE_CONNECTED; 951 } 952 return BluetoothProfile.STATE_DISCONNECTED; 953 } else { 954 Log.e(TAG, "Bad currentState: " + currentState); 955 return BluetoothProfile.STATE_DISCONNECTED; 956 } 957 } 958 } 959 960 List<BluetoothDevice> getConnectedDevices() { 961 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 962 synchronized(this) { 963 if (isConnected()) { 964 devices.add(mCurrentDevice); 965 } 966 } 967 return devices; 968 } 969 970 boolean isAudioOn() { 971 return (getCurrentState() == mAudioOn); 972 } 973 974 boolean isAudioConnected(BluetoothDevice device) { 975 synchronized(this) { 976 if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device)) { 977 return true; 978 } 979 } 980 return false; 981 } 982 983 int getAudioState(BluetoothDevice device) { 984 synchronized(this) { 985 if (mCurrentDevice == null || !mCurrentDevice.equals(device)) { 986 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 987 } 988 } 989 return mAudioState; 990 } 991 992 private void processVrEvent(int state) { 993 Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " + 994 mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " + mWaitingForVoiceRecognition + 995 " isInCall: " + isInCall()); 996 if (state == HeadsetHalConstants.VR_STATE_STARTED) { 997 // TODO(BT) handle virtualcall 998 if (!mVoiceRecognitionStarted && 999 !isInCall()) 1000 { 1001 try { 1002 mContext.startActivity(sVoiceCommandIntent); 1003 } catch (ActivityNotFoundException e) { 1004 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1005 return; 1006 } 1007 expectVoiceRecognition(); 1008 } 1009 } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) { 1010 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) 1011 { 1012 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK); 1013 mVoiceRecognitionStarted = false; 1014 mWaitingForVoiceRecognition = false; 1015 if (!isInCall()) 1016 disconnectAudioNative(getByteAddress(mCurrentDevice)); 1017 } 1018 else 1019 { 1020 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1021 } 1022 } else { 1023 Log.e(TAG, "Bad Voice Recognition state: " + state); 1024 } 1025 } 1026 1027 private void processLocalVrEvent(int state) 1028 { 1029 if (state == HeadsetHalConstants.VR_STATE_STARTED) 1030 { 1031 boolean needAudio = true; 1032 if (mVoiceRecognitionStarted || isInCall()) 1033 { 1034 Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() + 1035 " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 1036 return; 1037 } 1038 mVoiceRecognitionStarted = true; 1039 1040 if (mWaitingForVoiceRecognition) 1041 { 1042 Log.d(TAG, "Voice recognition started successfully"); 1043 mWaitingForVoiceRecognition = false; 1044 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK); 1045 removeMessages(START_VR_TIMEOUT); 1046 } 1047 else 1048 { 1049 Log.d(TAG, "Voice recognition started locally"); 1050 needAudio = startVoiceRecognitionNative(); 1051 } 1052 1053 if (needAudio && !isAudioOn()) 1054 { 1055 Log.d(TAG, "Initiating audio connection for Voice Recognition"); 1056 connectAudioNative(getByteAddress(mCurrentDevice)); 1057 } 1058 1059 if (mStartVoiceRecognitionWakeLock.isHeld()) { 1060 mStartVoiceRecognitionWakeLock.release(); 1061 } 1062 } 1063 else 1064 { 1065 Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " + mVoiceRecognitionStarted + 1066 " mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition); 1067 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) 1068 { 1069 mVoiceRecognitionStarted = false; 1070 mWaitingForVoiceRecognition = false; 1071 1072 if (stopVoiceRecognitionNative() && !isInCall()) 1073 disconnectAudioNative(getByteAddress(mCurrentDevice)); 1074 } 1075 } 1076 } 1077 1078 private synchronized void expectVoiceRecognition() { 1079 mWaitingForVoiceRecognition = true; 1080 sendMessageDelayed(START_VR_TIMEOUT, START_VR_TIMEOUT_VALUE); 1081 if (!mStartVoiceRecognitionWakeLock.isHeld()) { 1082 mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE); 1083 } 1084 } 1085 1086 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1087 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 1088 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 1089 int connectionState; 1090 synchronized (this) { 1091 for (BluetoothDevice device : bondedDevices) { 1092 ParcelUuid[] featureUuids = device.getUuids(); 1093 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 1094 continue; 1095 } 1096 connectionState = getConnectionState(device); 1097 for(int i = 0; i < states.length; i++) { 1098 if (connectionState == states[i]) { 1099 deviceList.add(device); 1100 } 1101 } 1102 } 1103 } 1104 return deviceList; 1105 } 1106 1107 // This method does not check for error conditon (newState == prevState) 1108 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 1109 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 1110 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1111 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1112 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1113 mContext.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 1114 if (DBG) log("Connection state " + device + ": " + prevState + "->" + newState); 1115 try { 1116 mAdapterService.sendConnectionStateChange(device, BluetoothProfile.HEADSET, newState, 1117 prevState); 1118 } catch (RemoteException e) { 1119 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1120 } 1121 } 1122 1123 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 1124 Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 1125 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1126 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1127 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1128 mContext.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 1129 if (DBG) log("Audio state " + device + ": " + prevState + "->" + newState); 1130 } 1131 1132 private void processAnswerCall() { 1133 if (mPhoneProxy != null) { 1134 try { 1135 mPhoneProxy.answerCall(); 1136 } catch (RemoteException e) { 1137 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1138 } 1139 } else { 1140 Log.e(TAG, "Handsfree phone proxy null for answering call"); 1141 } 1142 } 1143 1144 private void processHangupCall() { 1145 if (mPhoneProxy != null) { 1146 try { 1147 mPhoneProxy.hangupCall(); 1148 } catch (RemoteException e) { 1149 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1150 } 1151 } else { 1152 Log.e(TAG, "Handsfree phone proxy null for hanging up call"); 1153 } 1154 } 1155 1156 private void processDialCall(String number) { 1157 String dialNumber; 1158 if ((number == null) || (number.length() == 0)) { 1159 dialNumber = mPhonebook.getLastDialledNumber(); 1160 if (dialNumber == null) { 1161 if (DBG) log("processDialCall, last dial number null"); 1162 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1163 return; 1164 } 1165 } else if (number.charAt(0) == '>') { 1166 // Yuck - memory dialling requested. 1167 // Just dial last number for now 1168 if (number.startsWith(">9999")) { // for PTS test 1169 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1170 return; 1171 } 1172 if (DBG) log("processDialCall, memory dial do last dial for now"); 1173 dialNumber = mPhonebook.getLastDialledNumber(); 1174 if (dialNumber == null) { 1175 if (DBG) log("processDialCall, last dial number null"); 1176 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1177 return; 1178 } 1179 } else { 1180 // Remove trailing ';' 1181 if (number.charAt(number.length() - 1) == ';') { 1182 number = number.substring(0, number.length() - 1); 1183 } 1184 1185 dialNumber = PhoneNumberUtils.convertPreDial(number); 1186 } 1187 // TODO(BT) do we need to terminate virtual call first 1188 // like call terminateScoUsingVirtualVoiceCall()? 1189 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1190 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 1191 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1192 mContext.startActivity(intent); 1193 // TODO(BT) continue send OK reults code after call starts 1194 // hold wait lock, start a timer, set wait call flag 1195 // Get call started indication from bluetooth phone 1196 mDialingOut = true; 1197 sendMessageDelayed(DIALING_OUT_TIMEOUT, DIALING_OUT_TIMEOUT_VALUE); 1198 } 1199 1200 private void processVolumeEvent(int volumeType, int volume) { 1201 if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) { 1202 mPhoneState.setSpeakerVolume(volume); 1203 int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0; 1204 mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag); 1205 } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) { 1206 mPhoneState.setMicVolume(volume); 1207 } else { 1208 Log.e(TAG, "Bad voluem type: " + volumeType); 1209 } 1210 } 1211 1212 private void processSendDtmf(int dtmf) { 1213 if (mPhoneProxy != null) { 1214 try { 1215 mPhoneProxy.sendDtmf(dtmf); 1216 } catch (RemoteException e) { 1217 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1218 } 1219 } else { 1220 Log.e(TAG, "Handsfree phone proxy null for sending DTMF"); 1221 } 1222 } 1223 1224 private void processCallState(HeadsetCallState callState) { 1225 mPhoneState.setNumActiveCall(callState.mNumActive); 1226 mPhoneState.setNumHeldCall(callState.mNumHeld); 1227 mPhoneState.setCallState(callState.mCallState); 1228 if (mDialingOut && callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) { 1229 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK); 1230 removeMessages(DIALING_OUT_TIMEOUT); 1231 mDialingOut = false; 1232 } 1233 log("mNumActive: " + callState.mNumActive + " mNumHeld: " + callState.mNumHeld + 1234 " mCallState: " + callState.mCallState); 1235 log("mNumber: " + callState.mNumber + " mType: " + callState.mType); 1236 if (getCurrentState() != mDisconnected) { 1237 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, callState.mCallState, 1238 callState.mNumber, callState.mType); 1239 } 1240 } 1241 1242 private void processAtChld(int chld) { 1243 if (mPhoneProxy != null) { 1244 try { 1245 if (mPhoneProxy.processChld(chld)) { 1246 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK); 1247 } else { 1248 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1249 } 1250 } catch (RemoteException e) { 1251 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1252 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1253 } 1254 } else { 1255 Log.e(TAG, "Handsfree phone proxy null for At+Chld"); 1256 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1257 } 1258 } 1259 1260 private void processSubscriberNumberRequest() { 1261 if (mPhoneProxy != null) { 1262 try { 1263 String number = mPhoneProxy.getSubscriberNumber(); 1264 if (number != null) { 1265 atResponseStringNative("+CNUM: ,\"" + number + "\"," + 1266 PhoneNumberUtils.toaFromString(number) + ",,4"); 1267 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK); 1268 } 1269 } catch (RemoteException e) { 1270 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1271 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1272 } 1273 } else { 1274 Log.e(TAG, "Handsfree phone proxy null for At+CNUM"); 1275 } 1276 } 1277 1278 private void processAtCind() { 1279 cindResponseNative(mPhoneState.getService(), mPhoneState.getNumActiveCall(), 1280 mPhoneState.getNumHeldCall(), mPhoneState.getCallState(), 1281 mPhoneState.getSignal(), mPhoneState.getRoam(), 1282 mPhoneState.getBatteryCharge()); 1283 } 1284 1285 private void processAtCops() { 1286 if (mPhoneProxy != null) { 1287 try { 1288 String operatorName = mPhoneProxy.getNetworkOperator(); 1289 if (operatorName == null) { 1290 operatorName = ""; 1291 } 1292 copsResponseNative(operatorName); 1293 } catch (RemoteException e) { 1294 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1295 copsResponseNative(""); 1296 } 1297 } else { 1298 Log.e(TAG, "Handsfree phone proxy null for At+COPS"); 1299 copsResponseNative(""); 1300 } 1301 } 1302 1303 private void processAtClcc() { 1304 if (mPhoneProxy != null) { 1305 try { 1306 if (!mPhoneProxy.listCurrentCalls()) { 1307 clccResponseNative(0, 0, 0, 0, false, "", 0); 1308 } 1309 } catch (RemoteException e) { 1310 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1311 clccResponseNative(0, 0, 0, 0, false, "", 0); 1312 } 1313 } else { 1314 Log.e(TAG, "Handsfree phone proxy null for At+CLCC"); 1315 clccResponseNative(0, 0, 0, 0, false, "", 0); 1316 } 1317 } 1318 1319 private void processUnknownAt(String atString) { 1320 // TODO (BT) 1321 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1322 } 1323 1324 private void processKeyPressed() { 1325 if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) { 1326 if (mPhoneProxy != null) { 1327 try { 1328 mPhoneProxy.answerCall(); 1329 } catch (RemoteException e) { 1330 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1331 } 1332 } else { 1333 Log.e(TAG, "Handsfree phone proxy null for answering call"); 1334 } 1335 } else if (mPhoneState.getNumActiveCall() > 0) { 1336 if (!isAudioOn()) 1337 { 1338 connectAudioNative(getByteAddress(mCurrentDevice)); 1339 } 1340 else 1341 { 1342 if (mPhoneProxy != null) { 1343 try { 1344 mPhoneProxy.hangupCall(); 1345 } catch (RemoteException e) { 1346 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1347 } 1348 } else { 1349 Log.e(TAG, "Handsfree phone proxy null for hangup call"); 1350 } 1351 } 1352 } else { 1353 String dialNumber = mPhonebook.getLastDialledNumber(); 1354 if (dialNumber == null) { 1355 if (DBG) log("processKeyPressed, last dial number null"); 1356 return; 1357 } 1358 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1359 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 1360 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1361 mContext.startActivity(intent); 1362 } 1363 } 1364 1365 private void onConnectionStateChanged(int state, byte[] address) { 1366 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 1367 event.valueInt = state; 1368 event.device = getDevice(address); 1369 sendMessage(STACK_EVENT, event); 1370 } 1371 1372 private void onAudioStateChanged(int state, byte[] address) { 1373 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 1374 event.valueInt = state; 1375 event.device = getDevice(address); 1376 sendMessage(STACK_EVENT, event); 1377 } 1378 1379 private void onVrStateChanged(int state) { 1380 StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED); 1381 event.valueInt = state; 1382 sendMessage(STACK_EVENT, event); 1383 } 1384 1385 private void onAnswerCall() { 1386 StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL); 1387 sendMessage(STACK_EVENT, event); 1388 } 1389 1390 private void onHangupCall() { 1391 StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL); 1392 sendMessage(STACK_EVENT, event); 1393 } 1394 1395 private void onVolumeChanged(int type, int volume) { 1396 StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED); 1397 event.valueInt = type; 1398 event.valueInt2 = volume; 1399 sendMessage(STACK_EVENT, event); 1400 } 1401 1402 private void onDialCall(String number) { 1403 StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL); 1404 event.valueString = number; 1405 sendMessage(STACK_EVENT, event); 1406 } 1407 1408 private void onSendDtmf(int dtmf) { 1409 StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF); 1410 event.valueInt = dtmf; 1411 sendMessage(STACK_EVENT, event); 1412 } 1413 1414 private void onNoiceReductionEnable(boolean enable) { 1415 StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION); 1416 event.valueInt = enable ? 1 : 0; 1417 sendMessage(STACK_EVENT, event); 1418 } 1419 1420 private void onAtChld(int chld) { 1421 StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD); 1422 event.valueInt = chld; 1423 sendMessage(STACK_EVENT, event); 1424 } 1425 1426 private void onAtCnum() { 1427 StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST); 1428 sendMessage(STACK_EVENT, event); 1429 } 1430 1431 private void onAtCind() { 1432 StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND); 1433 sendMessage(STACK_EVENT, event); 1434 } 1435 1436 private void onAtCops() { 1437 StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS); 1438 sendMessage(STACK_EVENT, event); 1439 } 1440 1441 private void onAtClcc() { 1442 StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC); 1443 sendMessage(STACK_EVENT, event); 1444 } 1445 1446 private void onUnknownAt(String atString) { 1447 StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT); 1448 event.valueString = atString; 1449 sendMessage(STACK_EVENT, event); 1450 } 1451 1452 private void onKeyPressed() { 1453 StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED); 1454 sendMessage(STACK_EVENT, event); 1455 } 1456 1457 private void processIntentBatteryChanged(Intent intent) { 1458 int batteryLevel = intent.getIntExtra("level", -1); 1459 int scale = intent.getIntExtra("scale", -1); 1460 if (batteryLevel == -1 || scale == -1 || scale == 0) { 1461 Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale); 1462 return; 1463 } 1464 batteryLevel = batteryLevel * 5 / scale; 1465 mPhoneState.setBatteryCharge(batteryLevel); 1466 } 1467 1468 private void processRoamChanged(boolean roam) { 1469 mPhoneState.setRoam(roam ? HeadsetHalConstants.SERVICE_TYPE_ROAMING : 1470 HeadsetHalConstants.SERVICE_TYPE_HOME); 1471 } 1472 1473 private void processDeviceStateChanged(HeadsetDeviceState deviceState) { 1474 notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal, 1475 deviceState.mBatteryCharge); 1476 } 1477 1478 private void processSendClccResponse(HeadsetClccResponse clcc) { 1479 clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty, 1480 clcc.mNumber, clcc.mType); 1481 } 1482 1483 private String getCurrentDeviceName() { 1484 String defaultName = "<unknown>"; 1485 if (mCurrentDevice == null) { 1486 return defaultName; 1487 } 1488 String deviceName = mCurrentDevice.getName(); 1489 if (deviceName == null) { 1490 return defaultName; 1491 } 1492 return deviceName; 1493 } 1494 1495 private byte[] getByteAddress(BluetoothDevice device) { 1496 return Utils.getBytesFromAddress(device.getAddress()); 1497 } 1498 1499 private BluetoothDevice getDevice(byte[] address) { 1500 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 1501 } 1502 1503 private boolean isInCall() { 1504 return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) || 1505 (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE)); 1506 } 1507 1508 boolean isConnected() { 1509 IState currentState = getCurrentState(); 1510 return (currentState == mConnected || currentState == mAudioOn); 1511 } 1512 1513 private void log(String msg) { 1514 if (DBG) { 1515 Log.d(TAG, msg); 1516 } 1517 } 1518 1519 private static final String SCHEME_TEL = "tel"; 1520 1521 // Event types for STACK_EVENT message 1522 final private static int EVENT_TYPE_NONE = 0; 1523 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 1524 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 1525 final private static int EVENT_TYPE_VR_STATE_CHANGED = 3; 1526 final private static int EVENT_TYPE_ANSWER_CALL = 4; 1527 final private static int EVENT_TYPE_HANGUP_CALL = 5; 1528 final private static int EVENT_TYPE_VOLUME_CHANGED = 6; 1529 final private static int EVENT_TYPE_DIAL_CALL = 7; 1530 final private static int EVENT_TYPE_SEND_DTMF = 8; 1531 final private static int EVENT_TYPE_NOICE_REDUCTION = 9; 1532 final private static int EVENT_TYPE_AT_CHLD = 10; 1533 final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11; 1534 final private static int EVENT_TYPE_AT_CIND = 12; 1535 final private static int EVENT_TYPE_AT_COPS = 13; 1536 final private static int EVENT_TYPE_AT_CLCC = 14; 1537 final private static int EVENT_TYPE_UNKNOWN_AT = 15; 1538 final private static int EVENT_TYPE_KEY_PRESSED = 16; 1539 1540 private class StackEvent { 1541 int type = EVENT_TYPE_NONE; 1542 int valueInt = 0; 1543 int valueInt2 = 0; 1544 String valueString = null; 1545 BluetoothDevice device = null; 1546 1547 private StackEvent(int type) { 1548 this.type = type; 1549 } 1550 } 1551 1552 private native static void classInitNative(); 1553 private native void initializeNative(); 1554 private native void cleanupNative(); 1555 private native boolean connectHfpNative(byte[] address); 1556 private native boolean disconnectHfpNative(byte[] address); 1557 private native boolean connectAudioNative(byte[] address); 1558 private native boolean disconnectAudioNative(byte[] address); 1559 private native boolean startVoiceRecognitionNative(); 1560 private native boolean stopVoiceRecognitionNative(); 1561 private native boolean setVolumeNative(int volumeType, int volume); 1562 private native boolean cindResponseNative(int service, int numActive, int numHeld, 1563 int callState, int signal, int roam, 1564 int batteryCharge); 1565 private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal, 1566 int batteryCharge); 1567 private native boolean atResponseCodeNative(int responseCode); 1568 private native boolean clccResponseNative(int index, int dir, int status, int mode, 1569 boolean mpty, String number, int type); 1570 private native boolean copsResponseNative(String operatorName); 1571 private native boolean atResponseStringNative(String responseString); 1572 private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState, 1573 String number, int type); 1574} 1575