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