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