HeadsetStateMachine.java revision fd0cb64aeb1f806a041a5881dd2ba846b0aa6a93
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/** 18 * Bluetooth Handset StateMachine 19 * (Disconnected) 20 * | ^ 21 * CONNECT | | DISCONNECTED 22 * V | 23 * (Pending) 24 * | ^ 25 * CONNECTED | | CONNECT 26 * V | 27 * (Connected) 28 * | ^ 29 * CONNECT_AUDIO | | DISCONNECT_AUDIO 30 * V | 31 * (AudioOn) 32 */ 33package com.android.bluetooth.hfp; 34 35import android.bluetooth.BluetoothAdapter; 36import android.bluetooth.BluetoothAssignedNumbers; 37import android.bluetooth.BluetoothDevice; 38import android.bluetooth.BluetoothHeadset; 39import android.bluetooth.BluetoothProfile; 40import android.bluetooth.BluetoothUuid; 41import android.bluetooth.IBluetooth; 42import android.bluetooth.IBluetoothHeadsetPhone; 43import android.content.ComponentName; 44import android.content.Context; 45import android.content.Intent; 46import android.content.ServiceConnection; 47import android.content.ActivityNotFoundException; 48import android.media.AudioManager; 49import android.net.Uri; 50import android.os.IBinder; 51import android.os.Message; 52import android.os.ParcelUuid; 53import android.os.RemoteException; 54import android.os.ServiceManager; 55import android.os.PowerManager; 56import android.os.PowerManager.WakeLock; 57import android.telephony.PhoneNumberUtils; 58import android.util.Log; 59import com.android.bluetooth.Utils; 60import com.android.bluetooth.btservice.AdapterService; 61import com.android.internal.util.IState; 62import com.android.internal.util.State; 63import com.android.internal.util.StateMachine; 64import java.util.ArrayList; 65import java.util.List; 66import java.util.Set; 67 68final class HeadsetStateMachine extends StateMachine { 69 private static final String TAG = "HeadsetStateMachine"; 70 private static final boolean DBG = false; 71 //For Debugging only 72 private static int sRefCount=0; 73 74 private static final String HEADSET_NAME = "bt_headset_name"; 75 private static final String HEADSET_NREC = "bt_headset_nrec"; 76 77 static final int CONNECT = 1; 78 static final int DISCONNECT = 2; 79 static final int CONNECT_AUDIO = 3; 80 static final int DISCONNECT_AUDIO = 4; 81 static final int VOICE_RECOGNITION_START = 5; 82 static final int VOICE_RECOGNITION_STOP = 6; 83 84 // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION 85 // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO 86 static final int INTENT_SCO_VOLUME_CHANGED = 7; 87 static final int SET_MIC_VOLUME = 8; 88 static final int CALL_STATE_CHANGED = 9; 89 static final int INTENT_BATTERY_CHANGED = 10; 90 static final int DEVICE_STATE_CHANGED = 11; 91 static final int SEND_CCLC_RESPONSE = 12; 92 93 static final int VIRTUAL_CALL_START = 13; 94 static final int VIRTUAL_CALL_STOP = 14; 95 96 private static final int STACK_EVENT = 101; 97 private static final int DIALING_OUT_TIMEOUT = 102; 98 private static final int START_VR_TIMEOUT = 103; 99 100 private static final int CONNECT_TIMEOUT = 201; 101 102 private static final int DIALING_OUT_TIMEOUT_VALUE = 10000; 103 private static final int START_VR_TIMEOUT_VALUE = 5000; 104 105 private static final ParcelUuid[] HEADSET_UUIDS = { 106 BluetoothUuid.HSP, 107 BluetoothUuid.Handsfree, 108 }; 109 110 private Disconnected mDisconnected; 111 private Pending mPending; 112 private Connected mConnected; 113 private AudioOn mAudioOn; 114 115 private HeadsetService mService; 116 private PowerManager mPowerManager; 117 private boolean mVirtualCallStarted = false; 118 private boolean mVoiceRecognitionStarted = false; 119 private boolean mWaitingForVoiceRecognition = false; 120 private WakeLock mStartVoiceRecognitionWakeLock; // held while waiting for voice recognition 121 122 private boolean mDialingOut = false; 123 private AudioManager mAudioManager; 124 private AtPhonebook mPhonebook; 125 126 private static Intent sVoiceCommandIntent; 127 128 private HeadsetPhoneState mPhoneState; 129 private int mAudioState; 130 private BluetoothAdapter mAdapter; 131 private IBluetoothHeadsetPhone mPhoneProxy; 132 private boolean mNativeAvailable; 133 134 // mCurrentDevice is the device connected before the state changes 135 // mTargetDevice is the device to be connected 136 // mIncomingDevice is the device connecting to us, valid only in Pending state 137 // when mIncomingDevice is not null, both mCurrentDevice 138 // and mTargetDevice are null 139 // when either mCurrentDevice or mTargetDevice is not null, 140 // mIncomingDevice is null 141 // Stable states 142 // No connection, Disconnected state 143 // both mCurrentDevice and mTargetDevice are null 144 // Connected, Connected state 145 // mCurrentDevice is not null, mTargetDevice is null 146 // Interim states 147 // Connecting to a device, Pending 148 // mCurrentDevice is null, mTargetDevice is not null 149 // Disconnecting device, Connecting to new device 150 // Pending 151 // Both mCurrentDevice and mTargetDevice are not null 152 // Disconnecting device Pending 153 // mCurrentDevice is not null, mTargetDevice is null 154 // Incoming connections Pending 155 // Both mCurrentDevice and mTargetDevice are null 156 private BluetoothDevice mCurrentDevice = null; 157 private BluetoothDevice mTargetDevice = null; 158 private BluetoothDevice mIncomingDevice = null; 159 160 static { 161 classInitNative(); 162 } 163 164 private HeadsetStateMachine(HeadsetService context) { 165 super(TAG); 166 mService = context; 167 mVoiceRecognitionStarted = false; 168 mWaitingForVoiceRecognition = false; 169 170 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 171 mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 172 TAG + ":VoiceRecognition"); 173 mStartVoiceRecognitionWakeLock.setReferenceCounted(false); 174 175 mDialingOut = false; 176 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 177 mPhonebook = new AtPhonebook(mService, this); 178 mPhoneState = new HeadsetPhoneState(context, this); 179 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 180 mAdapter = BluetoothAdapter.getDefaultAdapter(); 181 if (!context.bindService(new Intent(IBluetoothHeadsetPhone.class.getName()), 182 mConnection, 0)) { 183 Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service"); 184 } 185 186 initializeNative(); 187 mNativeAvailable=true; 188 189 mDisconnected = new Disconnected(); 190 mPending = new Pending(); 191 mConnected = new Connected(); 192 mAudioOn = new AudioOn(); 193 194 if (sVoiceCommandIntent == null) { 195 sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND); 196 sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 197 } 198 199 addState(mDisconnected); 200 addState(mPending); 201 addState(mConnected); 202 addState(mAudioOn); 203 204 setInitialState(mDisconnected); 205 } 206 207 static HeadsetStateMachine make(HeadsetService context) { 208 Log.d(TAG, "make"); 209 HeadsetStateMachine hssm = new HeadsetStateMachine(context); 210 hssm.start(); 211 return hssm; 212 } 213 214 215 public void doQuit() { 216 quitNow(); 217 } 218 219 public void cleanup() { 220 if (mPhoneProxy != null) { 221 if (DBG) Log.d(TAG,"Unbinding service..."); 222 synchronized (mConnection) { 223 try { 224 mPhoneProxy = null; 225 mService.unbindService(mConnection); 226 } catch (Exception re) { 227 Log.e(TAG,"Error unbinding from IBluetoothHeadsetPhone",re); 228 } 229 } 230 } 231 if (mPhoneState != null) { 232 mPhoneState.listenForPhoneState(false); 233 mPhoneState.cleanup(); 234 } 235 if (mPhonebook != null) { 236 mPhonebook.cleanup(); 237 } 238 if (mNativeAvailable) { 239 cleanupNative(); 240 mNativeAvailable = false; 241 } 242 } 243 244 private class Disconnected extends State { 245 @Override 246 public void enter() { 247 log("Enter Disconnected: " + getCurrentMessage().what); 248 mPhonebook.resetAtState(); 249 mPhoneState.listenForPhoneState(false); 250 } 251 252 @Override 253 public boolean processMessage(Message message) { 254 log("Disconnected process message: " + message.what); 255 if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) { 256 Log.e(TAG, "ERROR: current, target, or mIncomingDevice not null in Disconnected"); 257 return NOT_HANDLED; 258 } 259 260 boolean retValue = HANDLED; 261 switch(message.what) { 262 case CONNECT: 263 BluetoothDevice device = (BluetoothDevice) message.obj; 264 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 265 BluetoothProfile.STATE_DISCONNECTED); 266 267 if (!connectHfpNative(getByteAddress(device)) ) { 268 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 269 BluetoothProfile.STATE_CONNECTING); 270 break; 271 } 272 273 synchronized (HeadsetStateMachine.this) { 274 mTargetDevice = device; 275 transitionTo(mPending); 276 } 277 // TODO(BT) remove CONNECT_TIMEOUT when the stack 278 // sends back events consistently 279 sendMessageDelayed(CONNECT_TIMEOUT, 30000); 280 break; 281 case DISCONNECT: 282 // ignore 283 break; 284 case INTENT_BATTERY_CHANGED: 285 processIntentBatteryChanged((Intent) message.obj); 286 break; 287 case CALL_STATE_CHANGED: 288 processCallState((HeadsetCallState) message.obj, 289 ((message.arg1 == 1)?true:false)); 290 break; 291 case STACK_EVENT: 292 StackEvent event = (StackEvent) message.obj; 293 if (DBG) { 294 log("event type: " + event.type); 295 } 296 switch (event.type) { 297 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 298 processConnectionEvent(event.valueInt, event.device); 299 break; 300 default: 301 Log.e(TAG, "Unexpected stack event: " + event.type); 302 break; 303 } 304 break; 305 default: 306 return NOT_HANDLED; 307 } 308 return retValue; 309 } 310 311 @Override 312 public void exit() { 313 log("Exit Disconnected: " + getCurrentMessage().what); 314 } 315 316 // in Disconnected state 317 private void processConnectionEvent(int state, BluetoothDevice device) { 318 switch (state) { 319 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 320 Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device); 321 break; 322 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 323 if (okToConnect(device)){ 324 Log.i(TAG,"Incoming Hf accepted"); 325 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 326 BluetoothProfile.STATE_DISCONNECTED); 327 synchronized (HeadsetStateMachine.this) { 328 mIncomingDevice = device; 329 transitionTo(mPending); 330 } 331 } else { 332 Log.i(TAG,"Incoming Hf rejected. priority=" + mService.getPriority(device)+ 333 " bondState=" + device.getBondState()); 334 //reject the connection and stay in Disconnected state itself 335 disconnectHfpNative(getByteAddress(device)); 336 // the other profile connection should be initiated 337 AdapterService adapterService = AdapterService.getAdapterService(); 338 if ( adapterService != null) { 339 adapterService.connectOtherProfile(device, 340 AdapterService.PROFILE_CONN_REJECTED); 341 } 342 } 343 break; 344 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 345 Log.w(TAG, "HFP Connected from Disconnected state"); 346 if (okToConnect(device)){ 347 Log.i(TAG,"Incoming Hf accepted"); 348 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 349 BluetoothProfile.STATE_DISCONNECTED); 350 synchronized (HeadsetStateMachine.this) { 351 mCurrentDevice = device; 352 transitionTo(mConnected); 353 } 354 configAudioParameters(); 355 } else { 356 //reject the connection and stay in Disconnected state itself 357 Log.i(TAG,"Incoming Hf rejected. priority=" + mService.getPriority(device) + 358 " bondState=" + device.getBondState()); 359 disconnectHfpNative(getByteAddress(device)); 360 // the other profile connection should be initiated 361 AdapterService adapterService = AdapterService.getAdapterService(); 362 if ( adapterService != null) { 363 adapterService.connectOtherProfile(device, 364 AdapterService.PROFILE_CONN_REJECTED); 365 } 366 } 367 break; 368 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 369 Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device); 370 break; 371 default: 372 Log.e(TAG, "Incorrect state: " + state); 373 break; 374 } 375 } 376 } 377 378 private class Pending extends State { 379 @Override 380 public void enter() { 381 log("Enter Pending: " + getCurrentMessage().what); 382 } 383 384 @Override 385 public boolean processMessage(Message message) { 386 log("Pending process message: " + message.what); 387 388 boolean retValue = HANDLED; 389 switch(message.what) { 390 case CONNECT: 391 case CONNECT_AUDIO: 392 deferMessage(message); 393 break; 394 case CONNECT_TIMEOUT: 395 onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, 396 getByteAddress(mTargetDevice)); 397 break; 398 case DISCONNECT: 399 BluetoothDevice device = (BluetoothDevice) message.obj; 400 if (mCurrentDevice != null && mTargetDevice != null && 401 mTargetDevice.equals(device) ) { 402 // cancel connection to the mTargetDevice 403 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 404 BluetoothProfile.STATE_CONNECTING); 405 synchronized (HeadsetStateMachine.this) { 406 mTargetDevice = null; 407 } 408 } else { 409 deferMessage(message); 410 } 411 break; 412 case INTENT_BATTERY_CHANGED: 413 processIntentBatteryChanged((Intent) message.obj); 414 break; 415 case CALL_STATE_CHANGED: 416 processCallState((HeadsetCallState) message.obj, 417 ((message.arg1 == 1)?true:false)); 418 break; 419 case STACK_EVENT: 420 StackEvent event = (StackEvent) message.obj; 421 if (DBG) { 422 log("event type: " + event.type); 423 } 424 switch (event.type) { 425 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 426 removeMessages(CONNECT_TIMEOUT); 427 processConnectionEvent(event.valueInt, event.device); 428 break; 429 default: 430 Log.e(TAG, "Unexpected event: " + event.type); 431 break; 432 } 433 break; 434 default: 435 return NOT_HANDLED; 436 } 437 return retValue; 438 } 439 440 // in Pending state 441 private void processConnectionEvent(int state, BluetoothDevice device) { 442 switch (state) { 443 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 444 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 445 broadcastConnectionState(mCurrentDevice, 446 BluetoothProfile.STATE_DISCONNECTED, 447 BluetoothProfile.STATE_DISCONNECTING); 448 synchronized (HeadsetStateMachine.this) { 449 mCurrentDevice = null; 450 } 451 452 if (mTargetDevice != null) { 453 if (!connectHfpNative(getByteAddress(mTargetDevice))) { 454 broadcastConnectionState(mTargetDevice, 455 BluetoothProfile.STATE_DISCONNECTED, 456 BluetoothProfile.STATE_CONNECTING); 457 synchronized (HeadsetStateMachine.this) { 458 mTargetDevice = null; 459 transitionTo(mDisconnected); 460 } 461 } 462 } else { 463 synchronized (HeadsetStateMachine.this) { 464 mIncomingDevice = null; 465 transitionTo(mDisconnected); 466 } 467 } 468 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 469 // outgoing connection failed 470 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 471 BluetoothProfile.STATE_CONNECTING); 472 synchronized (HeadsetStateMachine.this) { 473 mTargetDevice = null; 474 transitionTo(mDisconnected); 475 } 476 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 477 broadcastConnectionState(mIncomingDevice, 478 BluetoothProfile.STATE_DISCONNECTED, 479 BluetoothProfile.STATE_CONNECTING); 480 synchronized (HeadsetStateMachine.this) { 481 mIncomingDevice = null; 482 transitionTo(mDisconnected); 483 } 484 } else { 485 Log.e(TAG, "Unknown device Disconnected: " + device); 486 } 487 break; 488 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 489 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 490 // disconnection failed 491 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 492 BluetoothProfile.STATE_DISCONNECTING); 493 if (mTargetDevice != null) { 494 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 495 BluetoothProfile.STATE_CONNECTING); 496 } 497 synchronized (HeadsetStateMachine.this) { 498 mTargetDevice = null; 499 transitionTo(mConnected); 500 } 501 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 502 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 503 BluetoothProfile.STATE_CONNECTING); 504 synchronized (HeadsetStateMachine.this) { 505 mCurrentDevice = mTargetDevice; 506 mTargetDevice = null; 507 transitionTo(mConnected); 508 } 509 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 510 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 511 BluetoothProfile.STATE_CONNECTING); 512 synchronized (HeadsetStateMachine.this) { 513 mCurrentDevice = mIncomingDevice; 514 mIncomingDevice = null; 515 transitionTo(mConnected); 516 } 517 } else { 518 Log.e(TAG, "Unknown device Connected: " + device); 519 // something is wrong here, but sync our state with stack 520 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 521 BluetoothProfile.STATE_DISCONNECTED); 522 synchronized (HeadsetStateMachine.this) { 523 mCurrentDevice = device; 524 mTargetDevice = null; 525 mIncomingDevice = null; 526 transitionTo(mConnected); 527 } 528 } 529 configAudioParameters(); 530 break; 531 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 532 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 533 log("current device tries to connect back"); 534 // TODO(BT) ignore or reject 535 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 536 // The stack is connecting to target device or 537 // there is an incoming connection from the target device at the same time 538 // we already broadcasted the intent, doing nothing here 539 if (DBG) { 540 log("Stack and target device are connecting"); 541 } 542 } 543 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 544 Log.e(TAG, "Another connecting event on the incoming device"); 545 } else { 546 // We get an incoming connecting request while Pending 547 // TODO(BT) is stack handing this case? let's ignore it for now 548 log("Incoming connection while pending, ignore"); 549 } 550 break; 551 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 552 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 553 // we already broadcasted the intent, doing nothing here 554 if (DBG) { 555 log("stack is disconnecting mCurrentDevice"); 556 } 557 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 558 Log.e(TAG, "TargetDevice is getting disconnected"); 559 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 560 Log.e(TAG, "IncomingDevice is getting disconnected"); 561 } else { 562 Log.e(TAG, "Disconnecting unknow device: " + device); 563 } 564 break; 565 default: 566 Log.e(TAG, "Incorrect state: " + state); 567 break; 568 } 569 } 570 571 } 572 573 private class Connected extends State { 574 @Override 575 public void enter() { 576 log("Enter Connected: " + getCurrentMessage().what); 577 } 578 579 @Override 580 public boolean processMessage(Message message) { 581 log("Connected process message: " + message.what); 582 if (DBG) { 583 if (mCurrentDevice == null) { 584 log("ERROR: mCurrentDevice is null in Connected"); 585 return NOT_HANDLED; 586 } 587 } 588 589 boolean retValue = HANDLED; 590 switch(message.what) { 591 case CONNECT: 592 { 593 BluetoothDevice device = (BluetoothDevice) message.obj; 594 if (mCurrentDevice.equals(device)) { 595 break; 596 } 597 598 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 599 BluetoothProfile.STATE_DISCONNECTED); 600 if (!disconnectHfpNative(getByteAddress(mCurrentDevice))) { 601 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 602 BluetoothProfile.STATE_CONNECTING); 603 break; 604 } 605 606 synchronized (HeadsetStateMachine.this) { 607 mTargetDevice = device; 608 transitionTo(mPending); 609 } 610 } 611 break; 612 case DISCONNECT: 613 { 614 BluetoothDevice device = (BluetoothDevice) message.obj; 615 if (!mCurrentDevice.equals(device)) { 616 break; 617 } 618 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 619 BluetoothProfile.STATE_CONNECTED); 620 if (!disconnectHfpNative(getByteAddress(device))) { 621 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 622 BluetoothProfile.STATE_DISCONNECTED); 623 break; 624 } 625 transitionTo(mPending); 626 } 627 break; 628 case CONNECT_AUDIO: 629 // TODO(BT) when failure, broadcast audio connecting to disconnected intent 630 // check if device matches mCurrentDevice 631 connectAudioNative(getByteAddress(mCurrentDevice)); 632 break; 633 case VOICE_RECOGNITION_START: 634 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED); 635 break; 636 case VOICE_RECOGNITION_STOP: 637 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); 638 break; 639 case CALL_STATE_CHANGED: 640 processCallState((HeadsetCallState) message.obj, ((message.arg1==1)?true:false)); 641 break; 642 case INTENT_BATTERY_CHANGED: 643 processIntentBatteryChanged((Intent) message.obj); 644 break; 645 case DEVICE_STATE_CHANGED: 646 processDeviceStateChanged((HeadsetDeviceState) message.obj); 647 break; 648 case SEND_CCLC_RESPONSE: 649 processSendClccResponse((HeadsetClccResponse) message.obj); 650 break; 651 case DIALING_OUT_TIMEOUT: 652 if (mDialingOut) { 653 mDialingOut= false; 654 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 655 } 656 break; 657 case VIRTUAL_CALL_START: 658 initiateScoUsingVirtualVoiceCall(); 659 break; 660 case VIRTUAL_CALL_STOP: 661 terminateScoUsingVirtualVoiceCall(); 662 break; 663 case START_VR_TIMEOUT: 664 if (mWaitingForVoiceRecognition) { 665 mWaitingForVoiceRecognition = false; 666 Log.e(TAG, "Timeout waiting for voice recognition to start"); 667 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 668 } 669 break; 670 case STACK_EVENT: 671 StackEvent event = (StackEvent) message.obj; 672 if (DBG) { 673 log("event type: " + event.type); 674 } 675 switch (event.type) { 676 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 677 processConnectionEvent(event.valueInt, event.device); 678 break; 679 case EVENT_TYPE_AUDIO_STATE_CHANGED: 680 processAudioEvent(event.valueInt, event.device); 681 break; 682 case EVENT_TYPE_VR_STATE_CHANGED: 683 processVrEvent(event.valueInt); 684 break; 685 case EVENT_TYPE_ANSWER_CALL: 686 // TODO(BT) could answer call happen on Connected state? 687 processAnswerCall(); 688 break; 689 case EVENT_TYPE_HANGUP_CALL: 690 // TODO(BT) could hangup call happen on Connected state? 691 processHangupCall(); 692 break; 693 case EVENT_TYPE_VOLUME_CHANGED: 694 processVolumeEvent(event.valueInt, event.valueInt2); 695 break; 696 case EVENT_TYPE_DIAL_CALL: 697 processDialCall(event.valueString); 698 break; 699 case EVENT_TYPE_SEND_DTMF: 700 processSendDtmf(event.valueInt); 701 break; 702 case EVENT_TYPE_NOICE_REDUCTION: 703 processNoiceReductionEvent(event.valueInt); 704 break; 705 case EVENT_TYPE_AT_CHLD: 706 processAtChld(event.valueInt); 707 break; 708 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 709 processSubscriberNumberRequest(); 710 break; 711 case EVENT_TYPE_AT_CIND: 712 processAtCind(); 713 break; 714 case EVENT_TYPE_AT_COPS: 715 processAtCops(); 716 break; 717 case EVENT_TYPE_AT_CLCC: 718 processAtClcc(); 719 break; 720 case EVENT_TYPE_UNKNOWN_AT: 721 processUnknownAt(event.valueString); 722 break; 723 case EVENT_TYPE_KEY_PRESSED: 724 processKeyPressed(); 725 break; 726 default: 727 Log.e(TAG, "Unknown stack event: " + event.type); 728 break; 729 } 730 break; 731 default: 732 return NOT_HANDLED; 733 } 734 return retValue; 735 } 736 737 // in Connected state 738 private void processConnectionEvent(int state, BluetoothDevice device) { 739 switch (state) { 740 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 741 if (mCurrentDevice.equals(device)) { 742 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 743 BluetoothProfile.STATE_CONNECTED); 744 synchronized (HeadsetStateMachine.this) { 745 mCurrentDevice = null; 746 transitionTo(mDisconnected); 747 } 748 } else { 749 Log.e(TAG, "Disconnected from unknown device: " + device); 750 } 751 break; 752 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 753 processSlcConnected(); 754 break; 755 default: 756 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 757 break; 758 } 759 } 760 761 // in Connected state 762 private void processAudioEvent(int state, BluetoothDevice device) { 763 if (!mCurrentDevice.equals(device)) { 764 Log.e(TAG, "Audio changed on disconnected device: " + device); 765 return; 766 } 767 768 switch (state) { 769 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 770 // TODO(BT) should I save the state for next broadcast as the prevState? 771 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED; 772 mAudioManager.setBluetoothScoOn(true); 773 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED, 774 BluetoothHeadset.STATE_AUDIO_CONNECTING); 775 transitionTo(mAudioOn); 776 break; 777 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 778 mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING; 779 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING, 780 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 781 break; 782 // TODO(BT) process other states 783 default: 784 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 785 break; 786 } 787 } 788 789 private void processSlcConnected() { 790 if (mPhoneProxy != null) { 791 try { 792 // start phone state listener here, instead of on disconnected exit() 793 // On BT off, exitting SM sends a SM exit() call which incorrectly forces 794 // a listenForPhoneState(true). 795 // Additionally, no indicator updates should be sent prior to SLC setup 796 mPhoneState.listenForPhoneState(true); 797 mPhoneProxy.queryPhoneState(); 798 } catch (RemoteException e) { 799 Log.e(TAG, Log.getStackTraceString(new Throwable())); 800 } 801 } else { 802 Log.e(TAG, "Handsfree phone proxy null for query phone state"); 803 } 804 805 } 806 } 807 808 private class AudioOn extends State { 809 810 @Override 811 public void enter() { 812 log("Enter AudioOn: " + getCurrentMessage().what); 813 } 814 815 @Override 816 public boolean processMessage(Message message) { 817 log("AudioOn process message: " + message.what); 818 if (DBG) { 819 if (mCurrentDevice == null) { 820 log("ERROR: mCurrentDevice is null in AudioOn"); 821 return NOT_HANDLED; 822 } 823 } 824 825 boolean retValue = HANDLED; 826 switch(message.what) { 827 case DISCONNECT: 828 { 829 BluetoothDevice device = (BluetoothDevice) message.obj; 830 if (!mCurrentDevice.equals(device)) { 831 break; 832 } 833 deferMessage(obtainMessage(DISCONNECT, message.obj)); 834 } 835 // fall through 836 case DISCONNECT_AUDIO: 837 if (disconnectAudioNative(getByteAddress(mCurrentDevice))) { 838 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 839 mAudioManager.setBluetoothScoOn(false); 840 broadcastAudioState(mCurrentDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 841 BluetoothHeadset.STATE_AUDIO_CONNECTED); 842 } 843 break; 844 case VOICE_RECOGNITION_START: 845 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED); 846 break; 847 case VOICE_RECOGNITION_STOP: 848 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); 849 break; 850 case INTENT_SCO_VOLUME_CHANGED: 851 processIntentScoVolume((Intent) message.obj); 852 break; 853 case CALL_STATE_CHANGED: 854 processCallState((HeadsetCallState) message.obj, ((message.arg1 == 1)?true:false)); 855 break; 856 case INTENT_BATTERY_CHANGED: 857 processIntentBatteryChanged((Intent) message.obj); 858 break; 859 case DEVICE_STATE_CHANGED: 860 processDeviceStateChanged((HeadsetDeviceState) message.obj); 861 break; 862 case SEND_CCLC_RESPONSE: 863 processSendClccResponse((HeadsetClccResponse) message.obj); 864 break; 865 866 case VIRTUAL_CALL_START: 867 initiateScoUsingVirtualVoiceCall(); 868 break; 869 case VIRTUAL_CALL_STOP: 870 terminateScoUsingVirtualVoiceCall(); 871 break; 872 873 case DIALING_OUT_TIMEOUT: 874 if (mDialingOut) { 875 mDialingOut= false; 876 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 877 } 878 break; 879 case START_VR_TIMEOUT: 880 if (mWaitingForVoiceRecognition) { 881 mWaitingForVoiceRecognition = false; 882 Log.e(TAG, "Timeout waiting for voice recognition to start"); 883 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 884 } 885 break; 886 case STACK_EVENT: 887 StackEvent event = (StackEvent) message.obj; 888 if (DBG) { 889 log("event type: " + event.type); 890 } 891 switch (event.type) { 892 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 893 processConnectionEvent(event.valueInt, event.device); 894 break; 895 case EVENT_TYPE_AUDIO_STATE_CHANGED: 896 processAudioEvent(event.valueInt, event.device); 897 break; 898 case EVENT_TYPE_VR_STATE_CHANGED: 899 processVrEvent(event.valueInt); 900 break; 901 case EVENT_TYPE_ANSWER_CALL: 902 processAnswerCall(); 903 break; 904 case EVENT_TYPE_HANGUP_CALL: 905 processHangupCall(); 906 break; 907 case EVENT_TYPE_VOLUME_CHANGED: 908 processVolumeEvent(event.valueInt, event.valueInt2); 909 break; 910 case EVENT_TYPE_DIAL_CALL: 911 processDialCall(event.valueString); 912 break; 913 case EVENT_TYPE_SEND_DTMF: 914 processSendDtmf(event.valueInt); 915 break; 916 case EVENT_TYPE_NOICE_REDUCTION: 917 processNoiceReductionEvent(event.valueInt); 918 break; 919 case EVENT_TYPE_AT_CHLD: 920 processAtChld(event.valueInt); 921 break; 922 case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 923 processSubscriberNumberRequest(); 924 break; 925 case EVENT_TYPE_AT_CIND: 926 processAtCind(); 927 break; 928 case EVENT_TYPE_AT_COPS: 929 processAtCops(); 930 break; 931 case EVENT_TYPE_AT_CLCC: 932 processAtClcc(); 933 break; 934 case EVENT_TYPE_UNKNOWN_AT: 935 processUnknownAt(event.valueString); 936 break; 937 case EVENT_TYPE_KEY_PRESSED: 938 processKeyPressed(); 939 break; 940 default: 941 Log.e(TAG, "Unknown stack event: " + event.type); 942 break; 943 } 944 break; 945 default: 946 return NOT_HANDLED; 947 } 948 return retValue; 949 } 950 951 // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this 952 private void processConnectionEvent(int state, BluetoothDevice device) { 953 switch (state) { 954 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 955 if (mCurrentDevice.equals(device)) { 956 processAudioEvent (HeadsetHalConstants.AUDIO_STATE_DISCONNECTED, device); 957 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 958 BluetoothProfile.STATE_CONNECTED); 959 synchronized (HeadsetStateMachine.this) { 960 mCurrentDevice = null; 961 transitionTo(mDisconnected); 962 } 963 } else { 964 Log.e(TAG, "Disconnected from unknown device: " + device); 965 } 966 break; 967 default: 968 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 969 break; 970 } 971 } 972 973 // in AudioOn state 974 private void processAudioEvent(int state, BluetoothDevice device) { 975 if (!mCurrentDevice.equals(device)) { 976 Log.e(TAG, "Audio changed on disconnected device: " + device); 977 return; 978 } 979 980 switch (state) { 981 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 982 if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 983 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 984 mAudioManager.setBluetoothScoOn(false); 985 broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 986 BluetoothHeadset.STATE_AUDIO_CONNECTED); 987 } 988 transitionTo(mConnected); 989 break; 990 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 991 // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset? 992 //broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING, 993 // BluetoothHeadset.STATE_AUDIO_CONNECTED); 994 break; 995 default: 996 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 997 break; 998 } 999 } 1000 1001 private void processIntentScoVolume(Intent intent) { 1002 int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 1003 if (mPhoneState.getSpeakerVolume() != volumeValue) { 1004 mPhoneState.setSpeakerVolume(volumeValue); 1005 setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue); 1006 } 1007 } 1008 } 1009 1010 private ServiceConnection mConnection = new ServiceConnection() { 1011 public void onServiceConnected(ComponentName className, IBinder service) { 1012 if (DBG) Log.d(TAG, "Proxy object connected"); 1013 mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service); 1014 } 1015 1016 public void onServiceDisconnected(ComponentName className) { 1017 if (DBG) Log.d(TAG, "Proxy object disconnected"); 1018 mPhoneProxy = null; 1019 } 1020 }; 1021 1022 // HFP Connection state of the device could be changed by the state machine 1023 // in separate thread while this method is executing. 1024 int getConnectionState(BluetoothDevice device) { 1025 if (getCurrentState() == mDisconnected) { 1026 return BluetoothProfile.STATE_DISCONNECTED; 1027 } 1028 1029 synchronized (this) { 1030 IState currentState = getCurrentState(); 1031 if (currentState == mPending) { 1032 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 1033 return BluetoothProfile.STATE_CONNECTING; 1034 } 1035 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 1036 return BluetoothProfile.STATE_DISCONNECTING; 1037 } 1038 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 1039 return BluetoothProfile.STATE_CONNECTING; // incoming connection 1040 } 1041 return BluetoothProfile.STATE_DISCONNECTED; 1042 } 1043 1044 if (currentState == mConnected || currentState == mAudioOn) { 1045 if (mCurrentDevice.equals(device)) { 1046 return BluetoothProfile.STATE_CONNECTED; 1047 } 1048 return BluetoothProfile.STATE_DISCONNECTED; 1049 } else { 1050 Log.e(TAG, "Bad currentState: " + currentState); 1051 return BluetoothProfile.STATE_DISCONNECTED; 1052 } 1053 } 1054 } 1055 1056 List<BluetoothDevice> getConnectedDevices() { 1057 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 1058 synchronized(this) { 1059 if (isConnected()) { 1060 devices.add(mCurrentDevice); 1061 } 1062 } 1063 return devices; 1064 } 1065 1066 boolean isAudioOn() { 1067 return (getCurrentState() == mAudioOn); 1068 } 1069 1070 boolean isAudioConnected(BluetoothDevice device) { 1071 synchronized(this) { 1072 1073 /* Additional check for audio state included for the case when PhoneApp queries 1074 Bluetooth Audio state, before we receive the close event from the stack for the 1075 sco disconnect issued in AudioOn state. This was causing a mismatch in the 1076 Incall screen UI. */ 1077 1078 if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device) 1079 && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) 1080 { 1081 return true; 1082 } 1083 } 1084 return false; 1085 } 1086 1087 int getAudioState(BluetoothDevice device) { 1088 synchronized(this) { 1089 if (mCurrentDevice == null || !mCurrentDevice.equals(device)) { 1090 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 1091 } 1092 } 1093 return mAudioState; 1094 } 1095 1096 private void processVrEvent(int state) { 1097 Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " + 1098 mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " + mWaitingForVoiceRecognition + 1099 " isInCall: " + isInCall()); 1100 if (state == HeadsetHalConstants.VR_STATE_STARTED) { 1101 if (!mVoiceRecognitionStarted && 1102 !isVirtualCallInProgress() && 1103 !isInCall()) 1104 { 1105 try { 1106 mService.startActivity(sVoiceCommandIntent); 1107 } catch (ActivityNotFoundException e) { 1108 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1109 return; 1110 } 1111 expectVoiceRecognition(); 1112 } 1113 } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) { 1114 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) 1115 { 1116 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1117 mVoiceRecognitionStarted = false; 1118 mWaitingForVoiceRecognition = false; 1119 if (!isInCall()) { 1120 disconnectAudioNative(getByteAddress(mCurrentDevice)); 1121 mAudioManager.setParameters("A2dpSuspended=false"); 1122 } 1123 } 1124 else 1125 { 1126 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1127 } 1128 } else { 1129 Log.e(TAG, "Bad Voice Recognition state: " + state); 1130 } 1131 } 1132 1133 private void processLocalVrEvent(int state) 1134 { 1135 if (state == HeadsetHalConstants.VR_STATE_STARTED) 1136 { 1137 boolean needAudio = true; 1138 if (mVoiceRecognitionStarted || isInCall()) 1139 { 1140 Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() + 1141 " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 1142 return; 1143 } 1144 mVoiceRecognitionStarted = true; 1145 1146 if (mWaitingForVoiceRecognition) 1147 { 1148 Log.d(TAG, "Voice recognition started successfully"); 1149 mWaitingForVoiceRecognition = false; 1150 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1151 removeMessages(START_VR_TIMEOUT); 1152 } 1153 else 1154 { 1155 Log.d(TAG, "Voice recognition started locally"); 1156 needAudio = startVoiceRecognitionNative(); 1157 } 1158 1159 if (needAudio && !isAudioOn()) 1160 { 1161 Log.d(TAG, "Initiating audio connection for Voice Recognition"); 1162 // At this stage, we need to be sure that AVDTP is not streaming. This is needed 1163 // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in 1164 // streaming state while a SCO connection is established. 1165 // This is needed for VoiceDial scenario alone and not for 1166 // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE 1167 // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed. 1168 // Whereas for VoiceDial we want to activate the SCO connection but we are still 1169 // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream 1170 mAudioManager.setParameters("A2dpSuspended=true"); 1171 connectAudioNative(getByteAddress(mCurrentDevice)); 1172 } 1173 1174 if (mStartVoiceRecognitionWakeLock.isHeld()) { 1175 mStartVoiceRecognitionWakeLock.release(); 1176 } 1177 } 1178 else 1179 { 1180 Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " + mVoiceRecognitionStarted + 1181 " mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition); 1182 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) 1183 { 1184 mVoiceRecognitionStarted = false; 1185 mWaitingForVoiceRecognition = false; 1186 1187 if (stopVoiceRecognitionNative() && !isInCall()) { 1188 disconnectAudioNative(getByteAddress(mCurrentDevice)); 1189 mAudioManager.setParameters("A2dpSuspended=false"); 1190 } 1191 } 1192 } 1193 } 1194 1195 private synchronized void expectVoiceRecognition() { 1196 mWaitingForVoiceRecognition = true; 1197 sendMessageDelayed(START_VR_TIMEOUT, START_VR_TIMEOUT_VALUE); 1198 if (!mStartVoiceRecognitionWakeLock.isHeld()) { 1199 mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE); 1200 } 1201 } 1202 1203 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1204 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 1205 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 1206 int connectionState; 1207 synchronized (this) { 1208 for (BluetoothDevice device : bondedDevices) { 1209 ParcelUuid[] featureUuids = device.getUuids(); 1210 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 1211 continue; 1212 } 1213 connectionState = getConnectionState(device); 1214 for(int i = 0; i < states.length; i++) { 1215 if (connectionState == states[i]) { 1216 deviceList.add(device); 1217 } 1218 } 1219 } 1220 } 1221 return deviceList; 1222 } 1223 1224 // This method does not check for error conditon (newState == prevState) 1225 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 1226 log("Connection state " + device + ": " + prevState + "->" + newState); 1227 if(prevState == BluetoothProfile.STATE_CONNECTED) { 1228 // Headset is disconnecting, stop Virtual call if active. 1229 terminateScoUsingVirtualVoiceCall(); 1230 } 1231 1232 /* Notifying the connection state change of the profile before sending the intent for 1233 connection state change, as it was causing a race condition, with the UI not being 1234 updated with the correct connection state. */ 1235 mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET, 1236 newState, prevState); 1237 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 1238 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1239 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1240 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1241 mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 1242 } 1243 1244 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 1245 if(prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 1246 // When SCO gets disconnected during call transfer, Virtual call 1247 //needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall. 1248 terminateScoUsingVirtualVoiceCall(); 1249 } 1250 Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 1251 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1252 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1253 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1254 mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 1255 log("Audio state " + device + ": " + prevState + "->" + newState); 1256 } 1257 1258 /* 1259 * Put the AT command, company ID, arguments, and device in an Intent and broadcast it. 1260 */ 1261 private void broadcastVendorSpecificEventIntent(String command, 1262 int companyId, 1263 int commandType, 1264 Object[] arguments, 1265 BluetoothDevice device) { 1266 log("broadcastVendorSpecificEventIntent(" + command + ")"); 1267 Intent intent = 1268 new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT); 1269 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command); 1270 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, 1271 commandType); 1272 // assert: all elements of args are Serializable 1273 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments); 1274 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1275 1276 intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY 1277 + "." + Integer.toString(companyId)); 1278 1279 mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 1280 } 1281 1282 private void configAudioParameters() 1283 { 1284 // Reset NREC on connect event. Headset will override later 1285 mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName() + ";" + 1286 HEADSET_NREC + "=on"); 1287 } 1288 1289 private String parseUnknownAt(String atString) 1290 { 1291 StringBuilder atCommand = new StringBuilder(atString.length()); 1292 String result = null; 1293 1294 for (int i = 0; i < atString.length(); i++) { 1295 char c = atString.charAt(i); 1296 if (c == '"') { 1297 int j = atString.indexOf('"', i + 1 ); // search for closing " 1298 if (j == -1) { // unmatched ", insert one. 1299 atCommand.append(atString.substring(i, atString.length())); 1300 atCommand.append('"'); 1301 break; 1302 } 1303 atCommand.append(atString.substring(i, j + 1)); 1304 i = j; 1305 } else if (c != ' ') { 1306 atCommand.append(Character.toUpperCase(c)); 1307 } 1308 } 1309 result = atCommand.toString(); 1310 return result; 1311 } 1312 1313 private int getAtCommandType(String atCommand) 1314 { 1315 int commandType = mPhonebook.TYPE_UNKNOWN; 1316 String atString = null; 1317 atCommand = atCommand.trim(); 1318 if (atCommand.length() > 5) 1319 { 1320 atString = atCommand.substring(5); 1321 if (atString.startsWith("?")) // Read 1322 commandType = mPhonebook.TYPE_READ; 1323 else if (atString.startsWith("=?")) // Test 1324 commandType = mPhonebook.TYPE_TEST; 1325 else if (atString.startsWith("=")) // Set 1326 commandType = mPhonebook.TYPE_SET; 1327 else 1328 commandType = mPhonebook.TYPE_UNKNOWN; 1329 } 1330 return commandType; 1331 } 1332 1333 /* Method to check if Virtual Call in Progress */ 1334 private boolean isVirtualCallInProgress() { 1335 return mVirtualCallStarted; 1336 } 1337 1338 void setVirtualCallInProgress(boolean state) { 1339 mVirtualCallStarted = state; 1340 } 1341 1342 /* NOTE: Currently the VirtualCall API does not support handling of 1343 call transfers. If it is initiated from the handsfree device, 1344 HeadsetStateMachine will end the virtual call by calling 1345 terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */ 1346 synchronized boolean initiateScoUsingVirtualVoiceCall() { 1347 if (DBG) log("initiateScoUsingVirtualVoiceCall: Received"); 1348 // 1. Check if the SCO state is idle 1349 if (isInCall() || mVoiceRecognitionStarted) { 1350 Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress."); 1351 return false; 1352 } 1353 1354 // 2. Send virtual phone state changed to initialize SCO 1355 processCallState(new HeadsetCallState(0, 0, 1356 HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true); 1357 processCallState(new HeadsetCallState(0, 0, 1358 HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true); 1359 processCallState(new HeadsetCallState(1, 0, 1360 HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true); 1361 setVirtualCallInProgress(true); 1362 // Done 1363 if (DBG) log("initiateScoUsingVirtualVoiceCall: Done"); 1364 return true; 1365 } 1366 1367 synchronized boolean terminateScoUsingVirtualVoiceCall() { 1368 if (DBG) log("terminateScoUsingVirtualVoiceCall: Received"); 1369 1370 if (!isVirtualCallInProgress()) { 1371 Log.e(TAG, "terminateScoUsingVirtualVoiceCall:"+ 1372 "No present call to terminate"); 1373 return false; 1374 } 1375 1376 // 2. Send virtual phone state changed to close SCO 1377 processCallState(new HeadsetCallState(0, 0, 1378 HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true); 1379 setVirtualCallInProgress(false); 1380 // Done 1381 if (DBG) log("terminateScoUsingVirtualVoiceCall: Done"); 1382 return true; 1383 } 1384 1385 private void processAnswerCall() { 1386 if (mPhoneProxy != null) { 1387 try { 1388 mPhoneProxy.answerCall(); 1389 } catch (RemoteException e) { 1390 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1391 } 1392 } else { 1393 Log.e(TAG, "Handsfree phone proxy null for answering call"); 1394 } 1395 } 1396 1397 private void processHangupCall() { 1398 // Close the virtual call if active. Virtual call should be 1399 // terminated for CHUP callback event 1400 if (isVirtualCallInProgress()) { 1401 terminateScoUsingVirtualVoiceCall(); 1402 } else { 1403 if (mPhoneProxy != null) { 1404 try { 1405 mPhoneProxy.hangupCall(); 1406 } catch (RemoteException e) { 1407 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1408 } 1409 } else { 1410 Log.e(TAG, "Handsfree phone proxy null for hanging up call"); 1411 } 1412 } 1413 } 1414 1415 private void processDialCall(String number) { 1416 String dialNumber; 1417 if ((number == null) || (number.length() == 0)) { 1418 dialNumber = mPhonebook.getLastDialledNumber(); 1419 if (dialNumber == null) { 1420 if (DBG) log("processDialCall, last dial number null"); 1421 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1422 return; 1423 } 1424 } else if (number.charAt(0) == '>') { 1425 // Yuck - memory dialling requested. 1426 // Just dial last number for now 1427 if (number.startsWith(">9999")) { // for PTS test 1428 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1429 return; 1430 } 1431 if (DBG) log("processDialCall, memory dial do last dial for now"); 1432 dialNumber = mPhonebook.getLastDialledNumber(); 1433 if (dialNumber == null) { 1434 if (DBG) log("processDialCall, last dial number null"); 1435 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1436 return; 1437 } 1438 } else { 1439 // Remove trailing ';' 1440 if (number.charAt(number.length() - 1) == ';') { 1441 number = number.substring(0, number.length() - 1); 1442 } 1443 1444 dialNumber = PhoneNumberUtils.convertPreDial(number); 1445 } 1446 // Check for virtual call to terminate before sending Call Intent 1447 terminateScoUsingVirtualVoiceCall(); 1448 1449 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1450 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 1451 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1452 mService.startActivity(intent); 1453 // TODO(BT) continue send OK reults code after call starts 1454 // hold wait lock, start a timer, set wait call flag 1455 // Get call started indication from bluetooth phone 1456 mDialingOut = true; 1457 sendMessageDelayed(DIALING_OUT_TIMEOUT, DIALING_OUT_TIMEOUT_VALUE); 1458 } 1459 1460 private void processVolumeEvent(int volumeType, int volume) { 1461 if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) { 1462 mPhoneState.setSpeakerVolume(volume); 1463 int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0; 1464 mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag); 1465 } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) { 1466 mPhoneState.setMicVolume(volume); 1467 } else { 1468 Log.e(TAG, "Bad voluem type: " + volumeType); 1469 } 1470 } 1471 1472 private void processSendDtmf(int dtmf) { 1473 if (mPhoneProxy != null) { 1474 try { 1475 mPhoneProxy.sendDtmf(dtmf); 1476 } catch (RemoteException e) { 1477 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1478 } 1479 } else { 1480 Log.e(TAG, "Handsfree phone proxy null for sending DTMF"); 1481 } 1482 } 1483 1484 private void processCallState(HeadsetCallState callState) { 1485 processCallState(callState, false); 1486 } 1487 1488 private void processCallState(HeadsetCallState callState, 1489 boolean isVirtualCall) { 1490 mPhoneState.setNumActiveCall(callState.mNumActive); 1491 mPhoneState.setNumHeldCall(callState.mNumHeld); 1492 mPhoneState.setCallState(callState.mCallState); 1493 if (mDialingOut && callState.mCallState == 1494 HeadsetHalConstants.CALL_STATE_DIALING) { 1495 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1496 removeMessages(DIALING_OUT_TIMEOUT); 1497 mDialingOut = false; 1498 } 1499 log("mNumActive: " + callState.mNumActive + " mNumHeld: " + 1500 callState.mNumHeld +" mCallState: " + callState.mCallState); 1501 log("mNumber: " + callState.mNumber + " mType: " + callState.mType); 1502 if(!isVirtualCall) { 1503 /* Not a Virtual call request. End the virtual call, if running, 1504 before sending phoneStateChangeNative to BTIF */ 1505 terminateScoUsingVirtualVoiceCall(); 1506 } 1507 if (getCurrentState() != mDisconnected) { 1508 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 1509 callState.mCallState, callState.mNumber, callState.mType); 1510 } 1511 } 1512 1513 // enable 1 enable noice reduction 1514 // 0 disable noice reduction 1515 private void processNoiceReductionEvent(int enable) { 1516 if (enable == 1) { 1517 mAudioManager.setParameters(HEADSET_NREC + "=on"); 1518 } else { 1519 mAudioManager.setParameters(HEADSET_NREC + "=off"); 1520 } 1521 } 1522 1523 private void processAtChld(int chld) { 1524 if (mPhoneProxy != null) { 1525 try { 1526 if (mPhoneProxy.processChld(chld)) { 1527 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1528 } else { 1529 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1530 } 1531 } catch (RemoteException e) { 1532 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1533 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1534 } 1535 } else { 1536 Log.e(TAG, "Handsfree phone proxy null for At+Chld"); 1537 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1538 } 1539 } 1540 1541 private void processSubscriberNumberRequest() { 1542 if (mPhoneProxy != null) { 1543 try { 1544 String number = mPhoneProxy.getSubscriberNumber(); 1545 if (number != null) { 1546 atResponseStringNative("+CNUM: ,\"" + number + "\"," + 1547 PhoneNumberUtils.toaFromString(number) + ",,4"); 1548 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1549 } 1550 } catch (RemoteException e) { 1551 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1552 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1553 } 1554 } else { 1555 Log.e(TAG, "Handsfree phone proxy null for At+CNUM"); 1556 } 1557 } 1558 1559 private void processAtCind() { 1560 int call, call_setup; 1561 1562 /* Handsfree carkits expect that +CIND is properly responded to 1563 Hence we ensure that a proper response is sent 1564 for the virtual call too.*/ 1565 if (isVirtualCallInProgress()) { 1566 call = 1; 1567 call_setup = 0; 1568 } else { 1569 // regular phone call 1570 call = mPhoneState.getNumActiveCall(); 1571 call_setup = mPhoneState.getNumHeldCall(); 1572 } 1573 1574 cindResponseNative(mPhoneState.getService(), call, 1575 call_setup, mPhoneState.getCallState(), 1576 mPhoneState.getSignal(), mPhoneState.getRoam(), 1577 mPhoneState.getBatteryCharge()); 1578 } 1579 1580 private void processAtCops() { 1581 if (mPhoneProxy != null) { 1582 try { 1583 String operatorName = mPhoneProxy.getNetworkOperator(); 1584 if (operatorName == null) { 1585 operatorName = ""; 1586 } 1587 copsResponseNative(operatorName); 1588 } catch (RemoteException e) { 1589 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1590 copsResponseNative(""); 1591 } 1592 } else { 1593 Log.e(TAG, "Handsfree phone proxy null for At+COPS"); 1594 copsResponseNative(""); 1595 } 1596 } 1597 1598 private void processAtClcc() { 1599 if (mPhoneProxy != null) { 1600 try { 1601 if(isVirtualCallInProgress()) { 1602 String phoneNumber = ""; 1603 int type = PhoneNumberUtils.TOA_Unknown; 1604 try { 1605 phoneNumber = mPhoneProxy.getSubscriberNumber(); 1606 type = PhoneNumberUtils.toaFromString(phoneNumber); 1607 } catch (RemoteException ee) { 1608 Log.e(TAG, "Unable to retrieve phone number"+ 1609 "using IBluetoothHeadsetPhone proxy"); 1610 phoneNumber = ""; 1611 } 1612 clccResponseNative(1, 0, 0, 0, false, phoneNumber, type); 1613 } 1614 else if (!mPhoneProxy.listCurrentCalls()) { 1615 clccResponseNative(0, 0, 0, 0, false, "", 0); 1616 } 1617 } catch (RemoteException e) { 1618 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1619 clccResponseNative(0, 0, 0, 0, false, "", 0); 1620 } 1621 } else { 1622 Log.e(TAG, "Handsfree phone proxy null for At+CLCC"); 1623 clccResponseNative(0, 0, 0, 0, false, "", 0); 1624 } 1625 } 1626 1627 private void processAtCscs(String atString, int type) { 1628 log("processAtCscs - atString = "+ atString); 1629 if(mPhonebook != null) { 1630 mPhonebook.handleCscsCommand(atString, type); 1631 } 1632 else { 1633 Log.e(TAG, "Phonebook handle null for At+CSCS"); 1634 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1635 } 1636 } 1637 1638 private void processAtCpbs(String atString, int type) { 1639 log("processAtCpbs - atString = "+ atString); 1640 if(mPhonebook != null) { 1641 mPhonebook.handleCpbsCommand(atString, type); 1642 } 1643 else { 1644 Log.e(TAG, "Phonebook handle null for At+CPBS"); 1645 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1646 } 1647 } 1648 1649 private void processAtCpbr(String atString, int type, BluetoothDevice mCurrentDevice) { 1650 log("processAtCpbr - atString = "+ atString); 1651 if(mPhonebook != null) { 1652 mPhonebook.handleCpbrCommand(atString, type, mCurrentDevice); 1653 } 1654 else { 1655 Log.e(TAG, "Phonebook handle null for At+CPBR"); 1656 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1657 } 1658 } 1659 1660 /** 1661 * Find a character ch, ignoring quoted sections. 1662 * Return input.length() if not found. 1663 */ 1664 static private int findChar(char ch, String input, int fromIndex) { 1665 for (int i = fromIndex; i < input.length(); i++) { 1666 char c = input.charAt(i); 1667 if (c == '"') { 1668 i = input.indexOf('"', i + 1); 1669 if (i == -1) { 1670 return input.length(); 1671 } 1672 } else if (c == ch) { 1673 return i; 1674 } 1675 } 1676 return input.length(); 1677 } 1678 1679 /** 1680 * Break an argument string into individual arguments (comma delimited). 1681 * Integer arguments are turned into Integer objects. Otherwise a String 1682 * object is used. 1683 */ 1684 static private Object[] generateArgs(String input) { 1685 int i = 0; 1686 int j; 1687 ArrayList<Object> out = new ArrayList<Object>(); 1688 while (i <= input.length()) { 1689 j = findChar(',', input, i); 1690 1691 String arg = input.substring(i, j); 1692 try { 1693 out.add(new Integer(arg)); 1694 } catch (NumberFormatException e) { 1695 out.add(arg); 1696 } 1697 1698 i = j + 1; // move past comma 1699 } 1700 return out.toArray(); 1701 } 1702 1703 private void processAtXevent(String atString) { 1704 log("processAtXevent - atString = "+ atString); 1705 if (atString.startsWith("=") && !atString.startsWith("=?")) { 1706 Object[] args = generateArgs(atString.substring(1)); 1707 broadcastVendorSpecificEventIntent("+XEVENT", 1708 BluetoothAssignedNumbers.PLANTRONICS, 1709 BluetoothHeadset.AT_CMD_TYPE_SET, 1710 args, 1711 mCurrentDevice); 1712 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1713 } 1714 else { 1715 Log.e(TAG, "processAtXevent: command type error"); 1716 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1717 } 1718 } 1719 1720 private void processUnknownAt(String atString) { 1721 // TODO (BT) 1722 log("processUnknownAt - atString = "+ atString); 1723 String atCommand = parseUnknownAt(atString); 1724 int commandType = getAtCommandType(atCommand); 1725 if (atCommand.startsWith("+CSCS")) 1726 processAtCscs(atCommand.substring(5), commandType); 1727 else if (atCommand.startsWith("+CPBS")) 1728 processAtCpbs(atCommand.substring(5), commandType); 1729 else if (atCommand.startsWith("+CPBR")) 1730 processAtCpbr(atCommand.substring(5), commandType, mCurrentDevice); 1731 else if (atCommand.startsWith("+XEVENT")) 1732 processAtXevent(atCommand.substring(7)); 1733 else 1734 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1735 } 1736 1737 private void processKeyPressed() { 1738 if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) { 1739 if (mPhoneProxy != null) { 1740 try { 1741 mPhoneProxy.answerCall(); 1742 } catch (RemoteException e) { 1743 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1744 } 1745 } else { 1746 Log.e(TAG, "Handsfree phone proxy null for answering call"); 1747 } 1748 } else if (mPhoneState.getNumActiveCall() > 0) { 1749 if (!isAudioOn()) 1750 { 1751 connectAudioNative(getByteAddress(mCurrentDevice)); 1752 } 1753 else 1754 { 1755 if (mPhoneProxy != null) { 1756 try { 1757 mPhoneProxy.hangupCall(); 1758 } catch (RemoteException e) { 1759 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1760 } 1761 } else { 1762 Log.e(TAG, "Handsfree phone proxy null for hangup call"); 1763 } 1764 } 1765 } else { 1766 String dialNumber = mPhonebook.getLastDialledNumber(); 1767 if (dialNumber == null) { 1768 if (DBG) log("processKeyPressed, last dial number null"); 1769 return; 1770 } 1771 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1772 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 1773 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1774 mService.startActivity(intent); 1775 } 1776 } 1777 1778 private void onConnectionStateChanged(int state, byte[] address) { 1779 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 1780 event.valueInt = state; 1781 event.device = getDevice(address); 1782 sendMessage(STACK_EVENT, event); 1783 } 1784 1785 private void onAudioStateChanged(int state, byte[] address) { 1786 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 1787 event.valueInt = state; 1788 event.device = getDevice(address); 1789 sendMessage(STACK_EVENT, event); 1790 } 1791 1792 private void onVrStateChanged(int state) { 1793 StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED); 1794 event.valueInt = state; 1795 sendMessage(STACK_EVENT, event); 1796 } 1797 1798 private void onAnswerCall() { 1799 StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL); 1800 sendMessage(STACK_EVENT, event); 1801 } 1802 1803 private void onHangupCall() { 1804 StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL); 1805 sendMessage(STACK_EVENT, event); 1806 } 1807 1808 private void onVolumeChanged(int type, int volume) { 1809 StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED); 1810 event.valueInt = type; 1811 event.valueInt2 = volume; 1812 sendMessage(STACK_EVENT, event); 1813 } 1814 1815 private void onDialCall(String number) { 1816 StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL); 1817 event.valueString = number; 1818 sendMessage(STACK_EVENT, event); 1819 } 1820 1821 private void onSendDtmf(int dtmf) { 1822 StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF); 1823 event.valueInt = dtmf; 1824 sendMessage(STACK_EVENT, event); 1825 } 1826 1827 private void onNoiceReductionEnable(boolean enable) { 1828 StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION); 1829 event.valueInt = enable ? 1 : 0; 1830 sendMessage(STACK_EVENT, event); 1831 } 1832 1833 private void onAtChld(int chld) { 1834 StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD); 1835 event.valueInt = chld; 1836 sendMessage(STACK_EVENT, event); 1837 } 1838 1839 private void onAtCnum() { 1840 StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST); 1841 sendMessage(STACK_EVENT, event); 1842 } 1843 1844 private void onAtCind() { 1845 StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND); 1846 sendMessage(STACK_EVENT, event); 1847 } 1848 1849 private void onAtCops() { 1850 StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS); 1851 sendMessage(STACK_EVENT, event); 1852 } 1853 1854 private void onAtClcc() { 1855 StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC); 1856 sendMessage(STACK_EVENT, event); 1857 } 1858 1859 private void onUnknownAt(String atString) { 1860 StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT); 1861 event.valueString = atString; 1862 sendMessage(STACK_EVENT, event); 1863 } 1864 1865 private void onKeyPressed() { 1866 StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED); 1867 sendMessage(STACK_EVENT, event); 1868 } 1869 1870 private void processIntentBatteryChanged(Intent intent) { 1871 int batteryLevel = intent.getIntExtra("level", -1); 1872 int scale = intent.getIntExtra("scale", -1); 1873 if (batteryLevel == -1 || scale == -1 || scale == 0) { 1874 Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale); 1875 return; 1876 } 1877 batteryLevel = batteryLevel * 5 / scale; 1878 mPhoneState.setBatteryCharge(batteryLevel); 1879 } 1880 1881 private void processDeviceStateChanged(HeadsetDeviceState deviceState) { 1882 notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal, 1883 deviceState.mBatteryCharge); 1884 } 1885 1886 private void processSendClccResponse(HeadsetClccResponse clcc) { 1887 clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty, 1888 clcc.mNumber, clcc.mType); 1889 } 1890 1891 private String getCurrentDeviceName() { 1892 String defaultName = "<unknown>"; 1893 if (mCurrentDevice == null) { 1894 return defaultName; 1895 } 1896 String deviceName = mCurrentDevice.getName(); 1897 if (deviceName == null) { 1898 return defaultName; 1899 } 1900 return deviceName; 1901 } 1902 1903 private byte[] getByteAddress(BluetoothDevice device) { 1904 return Utils.getBytesFromAddress(device.getAddress()); 1905 } 1906 1907 private BluetoothDevice getDevice(byte[] address) { 1908 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 1909 } 1910 1911 private boolean isInCall() { 1912 return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) || 1913 (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE)); 1914 } 1915 1916 boolean isConnected() { 1917 IState currentState = getCurrentState(); 1918 return (currentState == mConnected || currentState == mAudioOn); 1919 } 1920 1921 boolean okToConnect(BluetoothDevice device) { 1922 AdapterService adapterService = AdapterService.getAdapterService(); 1923 int priority = mService.getPriority(device); 1924 boolean ret = false; 1925 //check if this is an incoming connection in Quiet mode. 1926 if((adapterService == null) || 1927 ((adapterService.isQuietModeEnabled() == true) && 1928 (mTargetDevice == null))){ 1929 ret = false; 1930 } 1931 // check priority and accept or reject the connection. if priority is undefined 1932 // it is likely that our SDP has not completed and peer is initiating the 1933 // connection. Allow this connection, provided the device is bonded 1934 else if((BluetoothProfile.PRIORITY_OFF < priority) || 1935 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 1936 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 1937 ret= true; 1938 } 1939 return ret; 1940 } 1941 1942 @Override 1943 protected void log(String msg) { 1944 if (DBG) { 1945 super.log(msg); 1946 } 1947 } 1948 1949 public void handleAccessPermissionResult(Intent intent) { 1950 log("handleAccessPermissionResult"); 1951 if(mPhonebook != null) { 1952 if (!mPhonebook.getCheckingAccessPermission()) { 1953 return; 1954 } 1955 int atCommandResult = 0; 1956 int atCommandErrorCode = 0; 1957 //HeadsetBase headset = mHandsfree.getHeadset(); 1958 // ASSERT: (headset != null) && headSet.isConnected() 1959 // REASON: mCheckingAccessPermission is true, otherwise resetAtState 1960 // has set mCheckingAccessPermission to false 1961 if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 1962 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 1963 BluetoothDevice.CONNECTION_ACCESS_NO) == 1964 BluetoothDevice.CONNECTION_ACCESS_YES) { 1965 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 1966 mCurrentDevice.setTrust(true); 1967 } 1968 atCommandResult = mPhonebook.processCpbrCommand(); 1969 } 1970 } 1971 mPhonebook.setCpbrIndex(-1); 1972 mPhonebook.setCheckingAccessPermission(false); 1973 1974 if (atCommandResult >= 0) { 1975 atResponseCodeNative(atCommandResult, atCommandErrorCode); 1976 } 1977 else 1978 log("handleAccessPermissionResult - RESULT_NONE"); 1979 } 1980 else { 1981 Log.e(TAG, "Phonebook handle null"); 1982 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1983 } 1984 } 1985 1986 private static final String SCHEME_TEL = "tel"; 1987 1988 // Event types for STACK_EVENT message 1989 final private static int EVENT_TYPE_NONE = 0; 1990 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 1991 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 1992 final private static int EVENT_TYPE_VR_STATE_CHANGED = 3; 1993 final private static int EVENT_TYPE_ANSWER_CALL = 4; 1994 final private static int EVENT_TYPE_HANGUP_CALL = 5; 1995 final private static int EVENT_TYPE_VOLUME_CHANGED = 6; 1996 final private static int EVENT_TYPE_DIAL_CALL = 7; 1997 final private static int EVENT_TYPE_SEND_DTMF = 8; 1998 final private static int EVENT_TYPE_NOICE_REDUCTION = 9; 1999 final private static int EVENT_TYPE_AT_CHLD = 10; 2000 final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11; 2001 final private static int EVENT_TYPE_AT_CIND = 12; 2002 final private static int EVENT_TYPE_AT_COPS = 13; 2003 final private static int EVENT_TYPE_AT_CLCC = 14; 2004 final private static int EVENT_TYPE_UNKNOWN_AT = 15; 2005 final private static int EVENT_TYPE_KEY_PRESSED = 16; 2006 2007 private class StackEvent { 2008 int type = EVENT_TYPE_NONE; 2009 int valueInt = 0; 2010 int valueInt2 = 0; 2011 String valueString = null; 2012 BluetoothDevice device = null; 2013 2014 private StackEvent(int type) { 2015 this.type = type; 2016 } 2017 } 2018 2019 /*package*/native boolean atResponseCodeNative(int responseCode, int errorCode); 2020 /*package*/ native boolean atResponseStringNative(String responseString); 2021 2022 private native static void classInitNative(); 2023 private native void initializeNative(); 2024 private native void cleanupNative(); 2025 private native boolean connectHfpNative(byte[] address); 2026 private native boolean disconnectHfpNative(byte[] address); 2027 private native boolean connectAudioNative(byte[] address); 2028 private native boolean disconnectAudioNative(byte[] address); 2029 private native boolean startVoiceRecognitionNative(); 2030 private native boolean stopVoiceRecognitionNative(); 2031 private native boolean setVolumeNative(int volumeType, int volume); 2032 private native boolean cindResponseNative(int service, int numActive, int numHeld, 2033 int callState, int signal, int roam, 2034 int batteryCharge); 2035 private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal, 2036 int batteryCharge); 2037 2038 private native boolean clccResponseNative(int index, int dir, int status, int mode, 2039 boolean mpty, String number, int type); 2040 private native boolean copsResponseNative(String operatorName); 2041 2042 private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState, 2043 String number, int type); 2044} 2045