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