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