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