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