HeadsetStateMachine.java revision bcbeaf69468424800a939b3e8678eaef21efa3d6
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 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 633 processSlcConnected(); 634 break; 635 default: 636 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 637 break; 638 } 639 } 640 641 // in Connected state 642 private void processAudioEvent(int state, BluetoothDevice device) { 643 if (!mCurrentDevice.equals(device)) { 644 Log.e(TAG, "Audio changed on disconnected device: " + device); 645 return; 646 } 647 648 switch (state) { 649 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 650 // TODO(BT) should I save the state for next broadcast as the prevState? 651 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED; 652 mAudioManager.setBluetoothScoOn(true); 653 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED, 654 BluetoothHeadset.STATE_AUDIO_CONNECTING); 655 transitionTo(mAudioOn); 656 break; 657 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 658 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING; 659 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING, 660 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 661 break; 662 // TODO(BT) process other states 663 default: 664 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 665 break; 666 } 667 } 668 669 private void processSlcConnected() { 670 if (mPhoneProxy != null) { 671 try { 672 mPhoneProxy.queryPhoneState(); 673 } catch (RemoteException e) { 674 Log.e(TAG, Log.getStackTraceString(new Throwable())); 675 } 676 } else { 677 Log.e(TAG, "Handsfree phone proxy null for query phone state"); 678 } 679 680 } 681 } 682 683 private class AudioOn extends State { 684 // Audio parameters 685 private static final String HEADSET_NAME = "bt_headset_name"; 686 private static final String HEADSET_NREC = "bt_headset_nrec"; 687 688 @Override 689 public void enter() { 690 log("Enter Audio: " + getCurrentMessage().what); 691 mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName() + ";" + 692 HEADSET_NREC + "=on"); 693 } 694 695 @Override 696 public boolean processMessage(Message message) { 697 log("AudioOn process message: " + message.what); 698 if (DBG) { 699 if (mCurrentDevice == null) { 700 log("ERROR: mCurrentDevice is null in AudioOn"); 701 return NOT_HANDLED; 702 } 703 } 704 705 boolean retValue = HANDLED; 706 switch(message.what) { 707 case DISCONNECT_AUDIO: 708 // TODO(BT) when failure broadcast a audio disconnecting to connected intent 709 // check if device matches mCurrentDevice 710 disconnectAudioNative(getByteAddress(mCurrentDevice)); 711 break; 712 case VOICE_RECOGNITION_START: 713 // TODO(BT) should we check if device matches mCurrentDevice? 714 startVoiceRecognitionNative(); 715 break; 716 case VOICE_RECOGNITION_STOP: 717 stopVoiceRecognitionNative(); 718 break; 719 case INTENT_SCO_VOLUME_CHANGED: 720 processIntentScoVolume((Intent) message.obj); 721 break; 722 case CALL_STATE_CHANGED: 723 processCallState((HeadsetCallState) message.obj); 724 break; 725 case INTENT_BATTERY_CHANGED: 726 processIntentBatteryChanged((Intent) message.obj); 727 break; 728 case ROAM_CHANGED: 729 processRoamChanged((Boolean) message.obj); 730 break; 731 case DEVICE_STATE_CHANGED: 732 processDeviceStateChanged((HeadsetDeviceState) message.obj); 733 break; 734 case SEND_CCLC_RESPONSE: 735 processSendClccResponse((HeadsetClccResponse) message.obj); 736 break; 737 case DIALING_OUT_TIMEOUT: 738 if (mDialingOut) { 739 mDialingOut= false; 740 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 741 } 742 break; 743 case STACK_EVENT: 744 StackEvent event = (StackEvent) message.obj; 745 if (DBG) { 746 log("event type: " + event.type); 747 } 748 switch (event.type) { 749 case EVENT_TYPE_AUDIO_STATE_CHANGED: 750 processAudioEvent(event.valueInt, event.device); 751 break; 752 case EVENT_TYPE_VR_STATE_CHANGED: 753 processVrEvent(event.valueInt); 754 break; 755 case EVENT_TYPE_ANSWER_CALL: 756 processAnswerCall(); 757 break; 758 case EVENT_TYPE_HANGUP_CALL: 759 processHangupCall(); 760 break; 761 case EVENT_TYPE_VOLUME_CHANGED: 762 processVolumeEvent(event.valueInt, event.valueInt2); 763 break; 764 case EVENT_TYPE_DIAL_CALL: 765 processDialCall(event.valueString); 766 break; 767 case EVENT_TYPE_SEND_DTMF: 768 processSendDtmf(event.valueInt); 769 break; 770 case EVENT_TYPE_NOICE_REDUCTION: 771 processNoiceReductionEvent(event.valueInt); 772 break; 773 case EVENT_TYPE_AT_CHLD: 774 processAtChld(event.valueInt); 775 break; 776 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 777 processSubscriberNumberRequest(); 778 break; 779 case EVENT_TYPE_AT_CIND: 780 processAtCind(); 781 break; 782 case EVENT_TYPE_AT_COPS: 783 processAtCops(); 784 break; 785 case EVENT_TYPE_AT_CLCC: 786 processAtClcc(); 787 break; 788 case EVENT_TYPE_UNKNOWN_AT: 789 processUnknownAt(event.valueString); 790 break; 791 case EVENT_TYPE_KEY_PRESSED: 792 processKeyPressed(); 793 break; 794 default: 795 Log.e(TAG, "Unknown stack event: " + event.type); 796 break; 797 } 798 break; 799 default: 800 return NOT_HANDLED; 801 } 802 return retValue; 803 } 804 805 // in AudioOn state 806 private void processAudioEvent(int state, BluetoothDevice device) { 807 if (!mCurrentDevice.equals(device)) { 808 Log.e(TAG, "Audio changed on disconnected device: " + device); 809 return; 810 } 811 812 switch (state) { 813 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 814 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 815 mAudioManager.setBluetoothScoOn(false); 816 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 817 BluetoothHeadset.STATE_AUDIO_CONNECTED); 818 transitionTo(mConnected); 819 break; 820 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 821 // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset? 822 //broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING, 823 // BluetoothHeadset.STATE_AUDIO_CONNECTED); 824 break; 825 default: 826 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 827 break; 828 } 829 } 830 831 private void processVrEvent(int state) { 832 if (state == HeadsetHalConstants.VR_STATE_STARTED) { 833 mVoiceRecognitionStarted = true; 834 // TODO(BT) should we send out Intent.ACTION_VOICE_COMMAND intent 835 // and do expectVoiceRecognition, acquire wake lock etc 836 } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) { 837 mVoiceRecognitionStarted = false; 838 } else { 839 Log.e(TAG, "Bad Voice Recognition state: " + state); 840 } 841 } 842 843 // enable 1 enable noice reduction 844 // 0 disable noice reduction 845 private void processNoiceReductionEvent(int enable) { 846 if (enable == 1) { 847 mAudioManager.setParameters(HEADSET_NREC + "=on"); 848 } else { 849 mAudioManager.setParameters(HEADSET_NREC + "off"); 850 } 851 } 852 853 private void processIntentScoVolume(Intent intent) { 854 int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 855 if (mPhoneState.getSpeakerVolume() != volumeValue) { 856 mPhoneState.setSpeakerVolume(volumeValue); 857 setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue); 858 } 859 } 860 } 861 862 private ServiceConnection mConnection = new ServiceConnection() { 863 public void onServiceConnected(ComponentName className, IBinder service) { 864 if (DBG) Log.d(TAG, "Proxy object connected"); 865 mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service); 866 } 867 868 public void onServiceDisconnected(ComponentName className) { 869 if (DBG) Log.d(TAG, "Proxy object disconnected"); 870 mPhoneProxy = null; 871 } 872 }; 873 874 // HFP Connection state of the device could be changed by the state machine 875 // in separate thread while this method is executing. 876 int getConnectionState(BluetoothDevice device) { 877 if (getCurrentState() == mDisconnected) { 878 return BluetoothProfile.STATE_DISCONNECTED; 879 } 880 881 synchronized (this) { 882 IState currentState = getCurrentState(); 883 if (currentState == mPending) { 884 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 885 return BluetoothProfile.STATE_CONNECTING; 886 } 887 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 888 return BluetoothProfile.STATE_DISCONNECTING; 889 } 890 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 891 return BluetoothProfile.STATE_CONNECTING; // incoming connection 892 } 893 return BluetoothProfile.STATE_DISCONNECTED; 894 } 895 896 if (currentState == mConnected || currentState == mAudioOn) { 897 if (mCurrentDevice.equals(device)) { 898 return BluetoothProfile.STATE_CONNECTED; 899 } 900 return BluetoothProfile.STATE_DISCONNECTED; 901 } else { 902 Log.e(TAG, "Bad currentState: " + currentState); 903 return BluetoothProfile.STATE_DISCONNECTED; 904 } 905 } 906 } 907 908 List<BluetoothDevice> getConnectedDevices() { 909 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 910 synchronized(this) { 911 if (isConnected()) { 912 devices.add(mCurrentDevice); 913 } 914 } 915 return devices; 916 } 917 918 boolean isAudioOn() { 919 return (getCurrentState() == mAudioOn); 920 } 921 922 boolean isAudioConnected(BluetoothDevice device) { 923 synchronized(this) { 924 if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device)) { 925 return true; 926 } 927 } 928 return false; 929 } 930 931 int getAudioState(BluetoothDevice device) { 932 synchronized(this) { 933 if (mCurrentDevice == null || !mCurrentDevice.equals(device)) { 934 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 935 } 936 } 937 return mAudioState; 938 } 939 940 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 941 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 942 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 943 int connectionState; 944 synchronized (this) { 945 for (BluetoothDevice device : bondedDevices) { 946 ParcelUuid[] featureUuids = device.getUuids(); 947 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 948 continue; 949 } 950 connectionState = getConnectionState(device); 951 for(int i = 0; i < states.length; i++) { 952 if (connectionState == states[i]) { 953 deviceList.add(device); 954 } 955 } 956 } 957 } 958 return deviceList; 959 } 960 961 // This method does not check for error conditon (newState == prevState) 962 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 963 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 964 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 965 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 966 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 967 mContext.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 968 if (DBG) log("Connection state " + device + ": " + prevState + "->" + newState); 969 try { 970 mAdapterService.sendConnectionStateChange(device, BluetoothProfile.HEADSET, newState, 971 prevState); 972 } catch (RemoteException e) { 973 Log.e(TAG, Log.getStackTraceString(new Throwable())); 974 } 975 } 976 977 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 978 Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 979 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 980 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 981 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 982 mContext.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 983 if (DBG) log("Audio state " + device + ": " + prevState + "->" + newState); 984 } 985 986 private void processAnswerCall() { 987 if (mPhoneProxy != null) { 988 try { 989 mPhoneProxy.answerCall(); 990 } catch (RemoteException e) { 991 Log.e(TAG, Log.getStackTraceString(new Throwable())); 992 } 993 } else { 994 Log.e(TAG, "Handsfree phone proxy null for answering call"); 995 } 996 } 997 998 private void processHangupCall() { 999 if (mPhoneProxy != null) { 1000 try { 1001 mPhoneProxy.hangupCall(); 1002 } catch (RemoteException e) { 1003 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1004 } 1005 } else { 1006 Log.e(TAG, "Handsfree phone proxy null for hanging up call"); 1007 } 1008 } 1009 1010 private void processDialCall(String number) { 1011 String dialNumber; 1012 if (number == null) { 1013 dialNumber = mPhonebook.getLastDialledNumber(); 1014 if (dialNumber == null) { 1015 if (DBG) log("processDialCall, last dial number null"); 1016 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1017 return; 1018 } 1019 } else if (number.charAt(0) == '>') { 1020 // Yuck - memory dialling requested. 1021 // Just dial last number for now 1022 if (number.startsWith(">9999")) { // for PTS test 1023 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1024 return; 1025 } 1026 if (DBG) log("processDialCall, memory dial do last dial for now"); 1027 dialNumber = mPhonebook.getLastDialledNumber(); 1028 if (dialNumber == null) { 1029 if (DBG) log("processDialCall, last dial number null"); 1030 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1031 return; 1032 } 1033 } else { 1034 dialNumber = PhoneNumberUtils.convertPreDial(number); 1035 } 1036 // TODO(BT) do we need to terminate virtual call first 1037 // like call terminateScoUsingVirtualVoiceCall()? 1038 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1039 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 1040 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1041 mContext.startActivity(intent); 1042 // TODO(BT) continue send OK reults code after call starts 1043 // hold wait lock, start a timer, set wait call flag 1044 // Get call started indication from bluetooth phone 1045 mDialingOut = true; 1046 sendMessageDelayed(DIALING_OUT_TIMEOUT, DIALING_OUT_TIMEOUT_VALUE); 1047 } 1048 1049 private void processVolumeEvent(int volumeType, int volume) { 1050 if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) { 1051 mPhoneState.setSpeakerVolume(volume); 1052 int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0; 1053 mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag); 1054 } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) { 1055 mPhoneState.setMicVolume(volume); 1056 } else { 1057 Log.e(TAG, "Bad voluem type: " + volumeType); 1058 } 1059 } 1060 1061 private void processSendDtmf(int dtmf) { 1062 if (mPhoneProxy != null) { 1063 try { 1064 mPhoneProxy.sendDtmf(dtmf); 1065 } catch (RemoteException e) { 1066 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1067 } 1068 } else { 1069 Log.e(TAG, "Handsfree phone proxy null for sending DTMF"); 1070 } 1071 } 1072 1073 private void processCallState(HeadsetCallState callState) { 1074 mPhoneState.setNumActiveCall(callState.mNumActive); 1075 mPhoneState.setNumHeldCall(callState.mNumHeld); 1076 mPhoneState.setCallState(callState.mCallState); 1077 if (mDialingOut && callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) { 1078 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK); 1079 removeMessages(DIALING_OUT_TIMEOUT); 1080 mDialingOut = false; 1081 } 1082 log("mNumActive: " + callState.mNumActive + " mNumHeld: " + callState.mNumHeld + 1083 " mCallState: " + callState.mCallState); 1084 log("mNumber: " + callState.mNumber + " mType: " + callState.mType); 1085 if (getCurrentState() != mDisconnected) { 1086 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, callState.mCallState, 1087 callState.mNumber, callState.mType); 1088 } 1089 } 1090 1091 private void processAtChld(int chld) { 1092 if (mPhoneProxy != null) { 1093 try { 1094 if (mPhoneProxy.processChld(chld)) { 1095 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK); 1096 } else { 1097 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1098 } 1099 } catch (RemoteException e) { 1100 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1101 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1102 } 1103 } else { 1104 Log.e(TAG, "Handsfree phone proxy null for At+Chld"); 1105 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1106 } 1107 } 1108 1109 private void processSubscriberNumberRequest() { 1110 if (mPhoneProxy != null) { 1111 try { 1112 String number = mPhoneProxy.getSubscriberNumber(); 1113 if (number != null) { 1114 atResponseStringNative("+CNUM: ,\"" + number + "\"," + 1115 PhoneNumberUtils.toaFromString(number) + ",,4"); 1116 } 1117 } catch (RemoteException e) { 1118 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1119 } 1120 } else { 1121 Log.e(TAG, "Handsfree phone proxy null for At+CNUM"); 1122 } 1123 } 1124 1125 private void processAtCind() { 1126 cindResponseNative(mPhoneState.getService(), mPhoneState.getNumActiveCall(), 1127 mPhoneState.getNumHeldCall(), mPhoneState.getCallState(), 1128 mPhoneState.getSignal(), mPhoneState.getRoam(), 1129 mPhoneState.getBatteryCharge()); 1130 } 1131 1132 private void processAtCops() { 1133 if (mPhoneProxy != null) { 1134 try { 1135 String operatorName = mPhoneProxy.getNetworkOperator(); 1136 if (operatorName == null) { 1137 operatorName = ""; 1138 } 1139 copsResponseNative(operatorName); 1140 } catch (RemoteException e) { 1141 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1142 copsResponseNative(""); 1143 } 1144 } else { 1145 Log.e(TAG, "Handsfree phone proxy null for At+COPS"); 1146 copsResponseNative(""); 1147 } 1148 } 1149 1150 private void processAtClcc() { 1151 if (mPhoneProxy != null) { 1152 try { 1153 if (!mPhoneProxy.listCurrentCalls()) { 1154 clccResponseNative(0, 0, 0, 0, false, "", 0); 1155 } 1156 } catch (RemoteException e) { 1157 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1158 clccResponseNative(0, 0, 0, 0, false, "", 0); 1159 } 1160 } else { 1161 Log.e(TAG, "Handsfree phone proxy null for At+CLCC"); 1162 clccResponseNative(0, 0, 0, 0, false, "", 0); 1163 } 1164 } 1165 1166 private void processUnknownAt(String atString) { 1167 // TODO (BT) 1168 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR); 1169 } 1170 1171 private void processKeyPressed() { 1172 if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) { 1173 if (mPhoneProxy != null) { 1174 try { 1175 mPhoneProxy.answerCall(); 1176 } catch (RemoteException e) { 1177 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1178 } 1179 } else { 1180 Log.e(TAG, "Handsfree phone proxy null for answering call"); 1181 } 1182 } else if (mPhoneState.getNumActiveCall() > 0) { 1183 if (mPhoneProxy != null) { 1184 try { 1185 mPhoneProxy.answerCall(); 1186 } catch (RemoteException e) { 1187 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1188 } 1189 } else { 1190 Log.e(TAG, "Handsfree phone proxy null for hangup call"); 1191 } 1192 } else { 1193 String dialNumber = mPhonebook.getLastDialledNumber(); 1194 if (dialNumber == null) { 1195 if (DBG) log("processKeyPressed, last dial number null"); 1196 return; 1197 } 1198 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1199 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 1200 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1201 mContext.startActivity(intent); 1202 } 1203 } 1204 1205 private void onConnectionStateChanged(int state, byte[] address) { 1206 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 1207 event.valueInt = state; 1208 event.device = getDevice(address); 1209 sendMessage(STACK_EVENT, event); 1210 } 1211 1212 private void onAudioStateChanged(int state, byte[] address) { 1213 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 1214 event.valueInt = state; 1215 event.device = getDevice(address); 1216 sendMessage(STACK_EVENT, event); 1217 } 1218 1219 private void onVrStateChanged(int state) { 1220 StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED); 1221 event.valueInt = state; 1222 sendMessage(STACK_EVENT, event); 1223 } 1224 1225 private void onAnswerCall() { 1226 StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL); 1227 sendMessage(STACK_EVENT, event); 1228 } 1229 1230 private void onHangupCall() { 1231 StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL); 1232 sendMessage(STACK_EVENT, event); 1233 } 1234 1235 private void onVolumeChanged(int type, int volume) { 1236 StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED); 1237 event.valueInt = type; 1238 event.valueInt2 = volume; 1239 sendMessage(STACK_EVENT, event); 1240 } 1241 1242 private void onDialCall(String number) { 1243 StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL); 1244 event.valueString = number; 1245 sendMessage(STACK_EVENT, event); 1246 } 1247 1248 private void onSendDtmf(int dtmf) { 1249 StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF); 1250 event.valueInt = dtmf; 1251 sendMessage(STACK_EVENT, event); 1252 } 1253 1254 private void onNoiceReductionEnable(boolean enable) { 1255 StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION); 1256 event.valueInt = enable ? 1 : 0; 1257 sendMessage(STACK_EVENT, event); 1258 } 1259 1260 private void onAtChld(int chld) { 1261 StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD); 1262 event.valueInt = chld; 1263 sendMessage(STACK_EVENT, event); 1264 } 1265 1266 private void onAtCnum() { 1267 StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST); 1268 sendMessage(STACK_EVENT, event); 1269 } 1270 1271 private void onAtCind() { 1272 StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND); 1273 sendMessage(STACK_EVENT, event); 1274 } 1275 1276 private void onAtCops() { 1277 StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS); 1278 sendMessage(STACK_EVENT, event); 1279 } 1280 1281 private void onAtClcc() { 1282 StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC); 1283 sendMessage(STACK_EVENT, event); 1284 } 1285 1286 private void onUnknownAt(String atString) { 1287 StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT); 1288 event.valueString = atString; 1289 sendMessage(STACK_EVENT, event); 1290 } 1291 1292 private void onKeyPressed() { 1293 StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED); 1294 sendMessage(STACK_EVENT, event); 1295 } 1296 1297 private void processIntentBatteryChanged(Intent intent) { 1298 int batteryLevel = intent.getIntExtra("level", -1); 1299 int scale = intent.getIntExtra("scale", -1); 1300 if (batteryLevel == -1 || scale == -1 || scale == 0) { 1301 Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale); 1302 return; 1303 } 1304 batteryLevel = batteryLevel * 5 / scale; 1305 mPhoneState.setBatteryCharge(batteryLevel); 1306 } 1307 1308 private void processRoamChanged(boolean roam) { 1309 mPhoneState.setRoam(roam ? HeadsetHalConstants.SERVICE_TYPE_ROAMING : 1310 HeadsetHalConstants.SERVICE_TYPE_HOME); 1311 } 1312 1313 private void processDeviceStateChanged(HeadsetDeviceState deviceState) { 1314 notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal, 1315 deviceState.mBatteryCharge); 1316 } 1317 1318 private void processSendClccResponse(HeadsetClccResponse clcc) { 1319 clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty, 1320 clcc.mNumber, clcc.mType); 1321 } 1322 1323 private String getCurrentDeviceName() { 1324 String defaultName = "<unknown>"; 1325 if (mCurrentDevice == null) { 1326 return defaultName; 1327 } 1328 String deviceName = mCurrentDevice.getName(); 1329 if (deviceName == null) { 1330 return defaultName; 1331 } 1332 return deviceName; 1333 } 1334 1335 private byte[] getByteAddress(BluetoothDevice device) { 1336 return Utils.getBytesFromAddress(device.getAddress()); 1337 } 1338 1339 private BluetoothDevice getDevice(byte[] address) { 1340 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 1341 } 1342 1343 private boolean isInCall() { 1344 return mPhoneState.isInCall(); 1345 } 1346 1347 boolean isConnected() { 1348 IState currentState = getCurrentState(); 1349 return (currentState == mConnected || currentState == mAudioOn); 1350 } 1351 1352 private void log(String msg) { 1353 if (DBG) { 1354 Log.d(TAG, msg); 1355 } 1356 } 1357 1358 private static final String SCHEME_TEL = "tel"; 1359 1360 // Event types for STACK_EVENT message 1361 final private static int EVENT_TYPE_NONE = 0; 1362 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 1363 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 1364 final private static int EVENT_TYPE_VR_STATE_CHANGED = 3; 1365 final private static int EVENT_TYPE_ANSWER_CALL = 4; 1366 final private static int EVENT_TYPE_HANGUP_CALL = 5; 1367 final private static int EVENT_TYPE_VOLUME_CHANGED = 6; 1368 final private static int EVENT_TYPE_DIAL_CALL = 7; 1369 final private static int EVENT_TYPE_SEND_DTMF = 8; 1370 final private static int EVENT_TYPE_NOICE_REDUCTION = 9; 1371 final private static int EVENT_TYPE_AT_CHLD = 10; 1372 final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11; 1373 final private static int EVENT_TYPE_AT_CIND = 12; 1374 final private static int EVENT_TYPE_AT_COPS = 13; 1375 final private static int EVENT_TYPE_AT_CLCC = 14; 1376 final private static int EVENT_TYPE_UNKNOWN_AT = 15; 1377 final private static int EVENT_TYPE_KEY_PRESSED = 16; 1378 1379 private class StackEvent { 1380 int type = EVENT_TYPE_NONE; 1381 int valueInt = 0; 1382 int valueInt2 = 0; 1383 String valueString = null; 1384 BluetoothDevice device = null; 1385 1386 private StackEvent(int type) { 1387 this.type = type; 1388 } 1389 } 1390 1391 private native static void classInitNative(); 1392 private native void initializeNativeDataNative(); 1393 private native boolean connectHfpNative(byte[] address); 1394 private native boolean disconnectHfpNative(byte[] address); 1395 private native boolean connectAudioNative(byte[] address); 1396 private native boolean disconnectAudioNative(byte[] address); 1397 private native boolean startVoiceRecognitionNative(); 1398 private native boolean stopVoiceRecognitionNative(); 1399 private native boolean setVolumeNative(int volumeType, int volume); 1400 private native boolean cindResponseNative(int service, int numActive, int numHeld, 1401 int callState, int signal, int roam, 1402 int batteryCharge); 1403 private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal, 1404 int batteryCharge); 1405 private native boolean atResponseCodeNative(int responseCode); 1406 private native boolean clccResponseNative(int index, int dir, int status, int mode, 1407 boolean mpty, String number, int type); 1408 private native boolean copsResponseNative(String operatorName); 1409 private native boolean atResponseStringNative(String responseString); 1410 private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState, 1411 String number, int type); 1412} 1413