HeadsetStateMachine.java revision 10260dc85133bbfbc51c53f06ba8992e00d9b67a
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.HashMap; 66import java.util.List; 67import java.util.Map; 68import java.util.Set; 69 70final 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 1521 if (isVirtualCall) { 1522 // virtual call state update 1523 if (getCurrentState() != mDisconnected) { 1524 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 1525 callState.mCallState, callState.mNumber, callState.mType); 1526 } 1527 } else { 1528 // circuit-switch voice call update 1529 // stop virtual voice call if there is a CSV call ongoing 1530 if (callState.mNumActive > 0 || callState.mNumHeld > 0 1531 || callState.mCallState != HeadsetHalConstants.CALL_STATE_IDLE) { 1532 terminateScoUsingVirtualVoiceCall(); 1533 } 1534 // at this step: if there is virtual call ongoing, it means there is no CSV call 1535 // let virtual call continue and skip phone state update 1536 if (!isVirtualCallInProgress()) { 1537 if (getCurrentState() != mDisconnected) { 1538 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 1539 callState.mCallState, callState.mNumber, callState.mType); 1540 } 1541 } 1542 } 1543 } 1544 1545 // enable 1 enable noice reduction 1546 // 0 disable noice reduction 1547 private void processNoiceReductionEvent(int enable) { 1548 if (enable == 1) { 1549 mAudioManager.setParameters(HEADSET_NREC + "=on"); 1550 } else { 1551 mAudioManager.setParameters(HEADSET_NREC + "=off"); 1552 } 1553 } 1554 1555 private void processAtChld(int chld) { 1556 if (mPhoneProxy != null) { 1557 try { 1558 if (mPhoneProxy.processChld(chld)) { 1559 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1560 } else { 1561 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1562 } 1563 } catch (RemoteException e) { 1564 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1565 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1566 } 1567 } else { 1568 Log.e(TAG, "Handsfree phone proxy null for At+Chld"); 1569 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1570 } 1571 } 1572 1573 private void processSubscriberNumberRequest() { 1574 if (mPhoneProxy != null) { 1575 try { 1576 String number = mPhoneProxy.getSubscriberNumber(); 1577 if (number != null) { 1578 atResponseStringNative("+CNUM: ,\"" + number + "\"," + 1579 PhoneNumberUtils.toaFromString(number) + ",,4"); 1580 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1581 } 1582 } catch (RemoteException e) { 1583 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1584 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1585 } 1586 } else { 1587 Log.e(TAG, "Handsfree phone proxy null for At+CNUM"); 1588 } 1589 } 1590 1591 private void processAtCind() { 1592 int call, call_setup; 1593 1594 /* Handsfree carkits expect that +CIND is properly responded to 1595 Hence we ensure that a proper response is sent 1596 for the virtual call too.*/ 1597 if (isVirtualCallInProgress()) { 1598 call = 1; 1599 call_setup = 0; 1600 } else { 1601 // regular phone call 1602 call = mPhoneState.getNumActiveCall(); 1603 call_setup = mPhoneState.getNumHeldCall(); 1604 } 1605 1606 cindResponseNative(mPhoneState.getService(), call, 1607 call_setup, mPhoneState.getCallState(), 1608 mPhoneState.getSignal(), mPhoneState.getRoam(), 1609 mPhoneState.getBatteryCharge()); 1610 } 1611 1612 private void processAtCops() { 1613 if (mPhoneProxy != null) { 1614 try { 1615 String operatorName = mPhoneProxy.getNetworkOperator(); 1616 if (operatorName == null) { 1617 operatorName = ""; 1618 } 1619 copsResponseNative(operatorName); 1620 } catch (RemoteException e) { 1621 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1622 copsResponseNative(""); 1623 } 1624 } else { 1625 Log.e(TAG, "Handsfree phone proxy null for At+COPS"); 1626 copsResponseNative(""); 1627 } 1628 } 1629 1630 private void processAtClcc() { 1631 if (mPhoneProxy != null) { 1632 try { 1633 if(isVirtualCallInProgress()) { 1634 String phoneNumber = ""; 1635 int type = PhoneNumberUtils.TOA_Unknown; 1636 try { 1637 phoneNumber = mPhoneProxy.getSubscriberNumber(); 1638 type = PhoneNumberUtils.toaFromString(phoneNumber); 1639 } catch (RemoteException ee) { 1640 Log.e(TAG, "Unable to retrieve phone number"+ 1641 "using IBluetoothHeadsetPhone proxy"); 1642 phoneNumber = ""; 1643 } 1644 clccResponseNative(1, 0, 0, 0, false, phoneNumber, type); 1645 clccResponseNative(0, 0, 0, 0, false, "", 0); 1646 } 1647 else if (!mPhoneProxy.listCurrentCalls()) { 1648 clccResponseNative(0, 0, 0, 0, false, "", 0); 1649 } 1650 } catch (RemoteException e) { 1651 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1652 clccResponseNative(0, 0, 0, 0, false, "", 0); 1653 } 1654 } else { 1655 Log.e(TAG, "Handsfree phone proxy null for At+CLCC"); 1656 clccResponseNative(0, 0, 0, 0, false, "", 0); 1657 } 1658 } 1659 1660 private void processAtCscs(String atString, int type) { 1661 log("processAtCscs - atString = "+ atString); 1662 if(mPhonebook != null) { 1663 mPhonebook.handleCscsCommand(atString, type); 1664 } 1665 else { 1666 Log.e(TAG, "Phonebook handle null for At+CSCS"); 1667 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1668 } 1669 } 1670 1671 private void processAtCpbs(String atString, int type) { 1672 log("processAtCpbs - atString = "+ atString); 1673 if(mPhonebook != null) { 1674 mPhonebook.handleCpbsCommand(atString, type); 1675 } 1676 else { 1677 Log.e(TAG, "Phonebook handle null for At+CPBS"); 1678 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1679 } 1680 } 1681 1682 private void processAtCpbr(String atString, int type, BluetoothDevice mCurrentDevice) { 1683 log("processAtCpbr - atString = "+ atString); 1684 if(mPhonebook != null) { 1685 mPhonebook.handleCpbrCommand(atString, type, mCurrentDevice); 1686 } 1687 else { 1688 Log.e(TAG, "Phonebook handle null for At+CPBR"); 1689 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1690 } 1691 } 1692 1693 /** 1694 * Find a character ch, ignoring quoted sections. 1695 * Return input.length() if not found. 1696 */ 1697 static private int findChar(char ch, String input, int fromIndex) { 1698 for (int i = fromIndex; i < input.length(); i++) { 1699 char c = input.charAt(i); 1700 if (c == '"') { 1701 i = input.indexOf('"', i + 1); 1702 if (i == -1) { 1703 return input.length(); 1704 } 1705 } else if (c == ch) { 1706 return i; 1707 } 1708 } 1709 return input.length(); 1710 } 1711 1712 /** 1713 * Break an argument string into individual arguments (comma delimited). 1714 * Integer arguments are turned into Integer objects. Otherwise a String 1715 * object is used. 1716 */ 1717 static private Object[] generateArgs(String input) { 1718 int i = 0; 1719 int j; 1720 ArrayList<Object> out = new ArrayList<Object>(); 1721 while (i <= input.length()) { 1722 j = findChar(',', input, i); 1723 1724 String arg = input.substring(i, j); 1725 try { 1726 out.add(new Integer(arg)); 1727 } catch (NumberFormatException e) { 1728 out.add(arg); 1729 } 1730 1731 i = j + 1; // move past comma 1732 } 1733 return out.toArray(); 1734 } 1735 1736 /** 1737 * @return {@code true} if the given string is a valid vendor-specific AT command. 1738 */ 1739 private boolean processVendorSpecificAt(String atString) { 1740 log("processVendorSpecificAt - atString = " + atString); 1741 1742 // Currently we accept only SET type commands. 1743 int indexOfEqual = atString.indexOf("="); 1744 if (indexOfEqual == -1) { 1745 Log.e(TAG, "processVendorSpecificAt: command type error in " + atString); 1746 return false; 1747 } 1748 1749 String command = atString.substring(0, indexOfEqual); 1750 Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command); 1751 if (companyId == null) { 1752 Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString); 1753 return false; 1754 } 1755 1756 String arg = atString.substring(indexOfEqual + 1); 1757 if (arg.startsWith("?")) { 1758 Log.e(TAG, "processVendorSpecificAt: command type error in " + atString); 1759 return false; 1760 } 1761 1762 Object[] args = generateArgs(arg); 1763 broadcastVendorSpecificEventIntent(command, 1764 companyId, 1765 BluetoothHeadset.AT_CMD_TYPE_SET, 1766 args, 1767 mCurrentDevice); 1768 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0); 1769 return true; 1770 } 1771 1772 private void processUnknownAt(String atString) { 1773 // TODO (BT) 1774 log("processUnknownAt - atString = "+ atString); 1775 String atCommand = parseUnknownAt(atString); 1776 int commandType = getAtCommandType(atCommand); 1777 if (atCommand.startsWith("+CSCS")) 1778 processAtCscs(atCommand.substring(5), commandType); 1779 else if (atCommand.startsWith("+CPBS")) 1780 processAtCpbs(atCommand.substring(5), commandType); 1781 else if (atCommand.startsWith("+CPBR")) 1782 processAtCpbr(atCommand.substring(5), commandType, mCurrentDevice); 1783 else if (!processVendorSpecificAt(atCommand)) 1784 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1785 } 1786 1787 private void processKeyPressed() { 1788 if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) { 1789 if (mPhoneProxy != null) { 1790 try { 1791 mPhoneProxy.answerCall(); 1792 } catch (RemoteException e) { 1793 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1794 } 1795 } else { 1796 Log.e(TAG, "Handsfree phone proxy null for answering call"); 1797 } 1798 } else if (mPhoneState.getNumActiveCall() > 0) { 1799 if (!isAudioOn()) 1800 { 1801 connectAudioNative(getByteAddress(mCurrentDevice)); 1802 } 1803 else 1804 { 1805 if (mPhoneProxy != null) { 1806 try { 1807 mPhoneProxy.hangupCall(); 1808 } catch (RemoteException e) { 1809 Log.e(TAG, Log.getStackTraceString(new Throwable())); 1810 } 1811 } else { 1812 Log.e(TAG, "Handsfree phone proxy null for hangup call"); 1813 } 1814 } 1815 } else { 1816 String dialNumber = mPhonebook.getLastDialledNumber(); 1817 if (dialNumber == null) { 1818 if (DBG) log("processKeyPressed, last dial number null"); 1819 return; 1820 } 1821 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1822 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 1823 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1824 mService.startActivity(intent); 1825 } 1826 } 1827 1828 private void onConnectionStateChanged(int state, byte[] address) { 1829 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 1830 event.valueInt = state; 1831 event.device = getDevice(address); 1832 sendMessage(STACK_EVENT, event); 1833 } 1834 1835 private void onAudioStateChanged(int state, byte[] address) { 1836 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 1837 event.valueInt = state; 1838 event.device = getDevice(address); 1839 sendMessage(STACK_EVENT, event); 1840 } 1841 1842 private void onVrStateChanged(int state) { 1843 StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED); 1844 event.valueInt = state; 1845 sendMessage(STACK_EVENT, event); 1846 } 1847 1848 private void onAnswerCall() { 1849 StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL); 1850 sendMessage(STACK_EVENT, event); 1851 } 1852 1853 private void onHangupCall() { 1854 StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL); 1855 sendMessage(STACK_EVENT, event); 1856 } 1857 1858 private void onVolumeChanged(int type, int volume) { 1859 StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED); 1860 event.valueInt = type; 1861 event.valueInt2 = volume; 1862 sendMessage(STACK_EVENT, event); 1863 } 1864 1865 private void onDialCall(String number) { 1866 StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL); 1867 event.valueString = number; 1868 sendMessage(STACK_EVENT, event); 1869 } 1870 1871 private void onSendDtmf(int dtmf) { 1872 StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF); 1873 event.valueInt = dtmf; 1874 sendMessage(STACK_EVENT, event); 1875 } 1876 1877 private void onNoiceReductionEnable(boolean enable) { 1878 StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION); 1879 event.valueInt = enable ? 1 : 0; 1880 sendMessage(STACK_EVENT, event); 1881 } 1882 1883 private void onAtChld(int chld) { 1884 StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD); 1885 event.valueInt = chld; 1886 sendMessage(STACK_EVENT, event); 1887 } 1888 1889 private void onAtCnum() { 1890 StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST); 1891 sendMessage(STACK_EVENT, event); 1892 } 1893 1894 private void onAtCind() { 1895 StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND); 1896 sendMessage(STACK_EVENT, event); 1897 } 1898 1899 private void onAtCops() { 1900 StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS); 1901 sendMessage(STACK_EVENT, event); 1902 } 1903 1904 private void onAtClcc() { 1905 StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC); 1906 sendMessage(STACK_EVENT, event); 1907 } 1908 1909 private void onUnknownAt(String atString) { 1910 StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT); 1911 event.valueString = atString; 1912 sendMessage(STACK_EVENT, event); 1913 } 1914 1915 private void onKeyPressed() { 1916 StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED); 1917 sendMessage(STACK_EVENT, event); 1918 } 1919 1920 private void processIntentBatteryChanged(Intent intent) { 1921 int batteryLevel = intent.getIntExtra("level", -1); 1922 int scale = intent.getIntExtra("scale", -1); 1923 if (batteryLevel == -1 || scale == -1 || scale == 0) { 1924 Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale); 1925 return; 1926 } 1927 batteryLevel = batteryLevel * 5 / scale; 1928 mPhoneState.setBatteryCharge(batteryLevel); 1929 } 1930 1931 private void processDeviceStateChanged(HeadsetDeviceState deviceState) { 1932 notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal, 1933 deviceState.mBatteryCharge); 1934 } 1935 1936 private void processSendClccResponse(HeadsetClccResponse clcc) { 1937 clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty, 1938 clcc.mNumber, clcc.mType); 1939 } 1940 1941 private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) { 1942 String stringToSend = resultCode.mCommand + ": "; 1943 if (resultCode.mArg != null) { 1944 stringToSend += resultCode.mArg; 1945 } 1946 atResponseStringNative(stringToSend); 1947 } 1948 1949 private String getCurrentDeviceName() { 1950 String defaultName = "<unknown>"; 1951 if (mCurrentDevice == null) { 1952 return defaultName; 1953 } 1954 String deviceName = mCurrentDevice.getName(); 1955 if (deviceName == null) { 1956 return defaultName; 1957 } 1958 return deviceName; 1959 } 1960 1961 private byte[] getByteAddress(BluetoothDevice device) { 1962 return Utils.getBytesFromAddress(device.getAddress()); 1963 } 1964 1965 private BluetoothDevice getDevice(byte[] address) { 1966 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 1967 } 1968 1969 private boolean isInCall() { 1970 return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) || 1971 (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE)); 1972 } 1973 1974 boolean isConnected() { 1975 IState currentState = getCurrentState(); 1976 return (currentState == mConnected || currentState == mAudioOn); 1977 } 1978 1979 boolean okToConnect(BluetoothDevice device) { 1980 AdapterService adapterService = AdapterService.getAdapterService(); 1981 int priority = mService.getPriority(device); 1982 boolean ret = false; 1983 //check if this is an incoming connection in Quiet mode. 1984 if((adapterService == null) || 1985 ((adapterService.isQuietModeEnabled() == true) && 1986 (mTargetDevice == null))){ 1987 ret = false; 1988 } 1989 // check priority and accept or reject the connection. if priority is undefined 1990 // it is likely that our SDP has not completed and peer is initiating the 1991 // connection. Allow this connection, provided the device is bonded 1992 else if((BluetoothProfile.PRIORITY_OFF < priority) || 1993 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 1994 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 1995 ret= true; 1996 } 1997 return ret; 1998 } 1999 2000 @Override 2001 protected void log(String msg) { 2002 if (DBG) { 2003 super.log(msg); 2004 } 2005 } 2006 2007 public void handleAccessPermissionResult(Intent intent) { 2008 log("handleAccessPermissionResult"); 2009 if(mPhonebook != null) { 2010 if (!mPhonebook.getCheckingAccessPermission()) { 2011 return; 2012 } 2013 int atCommandResult = 0; 2014 int atCommandErrorCode = 0; 2015 //HeadsetBase headset = mHandsfree.getHeadset(); 2016 // ASSERT: (headset != null) && headSet.isConnected() 2017 // REASON: mCheckingAccessPermission is true, otherwise resetAtState 2018 // has set mCheckingAccessPermission to false 2019 if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 2020 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 2021 BluetoothDevice.CONNECTION_ACCESS_NO) == 2022 BluetoothDevice.CONNECTION_ACCESS_YES) { 2023 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 2024 mCurrentDevice.setTrust(true); 2025 } 2026 atCommandResult = mPhonebook.processCpbrCommand(); 2027 } 2028 } 2029 mPhonebook.setCpbrIndex(-1); 2030 mPhonebook.setCheckingAccessPermission(false); 2031 2032 if (atCommandResult >= 0) { 2033 atResponseCodeNative(atCommandResult, atCommandErrorCode); 2034 } 2035 else 2036 log("handleAccessPermissionResult - RESULT_NONE"); 2037 } 2038 else { 2039 Log.e(TAG, "Phonebook handle null"); 2040 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2041 } 2042 } 2043 2044 private static final String SCHEME_TEL = "tel"; 2045 2046 // Event types for STACK_EVENT message 2047 final private static int EVENT_TYPE_NONE = 0; 2048 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 2049 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 2050 final private static int EVENT_TYPE_VR_STATE_CHANGED = 3; 2051 final private static int EVENT_TYPE_ANSWER_CALL = 4; 2052 final private static int EVENT_TYPE_HANGUP_CALL = 5; 2053 final private static int EVENT_TYPE_VOLUME_CHANGED = 6; 2054 final private static int EVENT_TYPE_DIAL_CALL = 7; 2055 final private static int EVENT_TYPE_SEND_DTMF = 8; 2056 final private static int EVENT_TYPE_NOICE_REDUCTION = 9; 2057 final private static int EVENT_TYPE_AT_CHLD = 10; 2058 final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11; 2059 final private static int EVENT_TYPE_AT_CIND = 12; 2060 final private static int EVENT_TYPE_AT_COPS = 13; 2061 final private static int EVENT_TYPE_AT_CLCC = 14; 2062 final private static int EVENT_TYPE_UNKNOWN_AT = 15; 2063 final private static int EVENT_TYPE_KEY_PRESSED = 16; 2064 2065 private class StackEvent { 2066 int type = EVENT_TYPE_NONE; 2067 int valueInt = 0; 2068 int valueInt2 = 0; 2069 String valueString = null; 2070 BluetoothDevice device = null; 2071 2072 private StackEvent(int type) { 2073 this.type = type; 2074 } 2075 } 2076 2077 /*package*/native boolean atResponseCodeNative(int responseCode, int errorCode); 2078 /*package*/ native boolean atResponseStringNative(String responseString); 2079 2080 private native static void classInitNative(); 2081 private native void initializeNative(); 2082 private native void cleanupNative(); 2083 private native boolean connectHfpNative(byte[] address); 2084 private native boolean disconnectHfpNative(byte[] address); 2085 private native boolean connectAudioNative(byte[] address); 2086 private native boolean disconnectAudioNative(byte[] address); 2087 private native boolean startVoiceRecognitionNative(); 2088 private native boolean stopVoiceRecognitionNative(); 2089 private native boolean setVolumeNative(int volumeType, int volume); 2090 private native boolean cindResponseNative(int service, int numActive, int numHeld, 2091 int callState, int signal, int roam, 2092 int batteryCharge); 2093 private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal, 2094 int batteryCharge); 2095 2096 private native boolean clccResponseNative(int index, int dir, int status, int mode, 2097 boolean mpty, String number, int type); 2098 private native boolean copsResponseNative(String operatorName); 2099 2100 private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState, 2101 String number, int type); 2102} 2103