HeadsetStateMachine.java revision 1f7304a0df9fa7e1ccee0cd9a61933a0f9be3e8c
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 17package com.android.bluetooth.hfp; 18 19import android.bluetooth.BluetoothAdapter; 20import android.bluetooth.BluetoothAssignedNumbers; 21import android.bluetooth.BluetoothDevice; 22import android.bluetooth.BluetoothHeadset; 23import android.bluetooth.BluetoothProfile; 24import android.bluetooth.BluetoothUuid; 25import android.content.ActivityNotFoundException; 26import android.content.Context; 27import android.content.Intent; 28import android.media.AudioManager; 29import android.net.Uri; 30import android.os.IDeviceIdleController; 31import android.os.Looper; 32import android.os.Message; 33import android.os.ParcelUuid; 34import android.os.RemoteException; 35import android.os.ServiceManager; 36import android.os.UserHandle; 37import android.support.annotation.VisibleForTesting; 38import android.telephony.PhoneNumberUtils; 39import android.util.Log; 40 41import com.android.bluetooth.btservice.AdapterService; 42import com.android.bluetooth.btservice.ProfileService; 43import com.android.internal.util.State; 44import com.android.internal.util.StateMachine; 45 46import java.io.FileDescriptor; 47import java.io.PrintWriter; 48import java.io.StringWriter; 49import java.util.ArrayList; 50import java.util.HashMap; 51import java.util.List; 52import java.util.Map; 53import java.util.Set; 54 55/** 56 * Bluetooth Handset StateMachine 57 * (Disconnected) 58 * | ^ 59 * CONNECT | | DISCONNECTED 60 * V | 61 * (Connecting) (Disconnecting) 62 * | ^ 63 * CONNECTED | | DISCONNECT 64 * V | 65 * (Connected) 66 * | ^ 67 * CONNECT_AUDIO | | AUDIO_DISCONNECTED 68 * V | 69 * (AudioConnecting) (AudioDiconnecting) 70 * | ^ 71 * AUDIO_CONNECTED | | DISCONNECT_AUDIO 72 * V | 73 * (AudioOn) 74 */ 75final class HeadsetStateMachine extends StateMachine { 76 private static final String TAG = "HeadsetStateMachine"; 77 private static final boolean DBG = false; 78 79 private static final String HEADSET_NAME = "bt_headset_name"; 80 private static final String HEADSET_NREC = "bt_headset_nrec"; 81 private static final String HEADSET_WBS = "bt_wbs"; 82 83 /* Telephone URI scheme */ 84 private static final String SCHEME_TEL = "tel"; 85 86 static final int CONNECT = 1; 87 static final int DISCONNECT = 2; 88 static final int CONNECT_AUDIO = 3; 89 static final int DISCONNECT_AUDIO = 4; 90 static final int VOICE_RECOGNITION_START = 5; 91 static final int VOICE_RECOGNITION_STOP = 6; 92 93 // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION 94 // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO 95 static final int INTENT_SCO_VOLUME_CHANGED = 7; 96 static final int INTENT_CONNECTION_ACCESS_REPLY = 8; 97 static final int CALL_STATE_CHANGED = 9; 98 static final int DEVICE_STATE_CHANGED = 11; 99 static final int SEND_CCLC_RESPONSE = 12; 100 static final int SEND_VENDOR_SPECIFIC_RESULT_CODE = 13; 101 102 static final int VIRTUAL_CALL_START = 14; 103 static final int VIRTUAL_CALL_STOP = 15; 104 105 static final int STACK_EVENT = 101; 106 private static final int DIALING_OUT_TIMEOUT = 102; 107 private static final int START_VR_TIMEOUT = 103; 108 private static final int CLCC_RSP_TIMEOUT = 104; 109 110 private static final int CONNECT_TIMEOUT = 201; 111 112 private static final int DIALING_OUT_TIMEOUT_VALUE = 10000; 113 private static final int START_VR_TIMEOUT_VALUE = 5000; 114 private static final int CLCC_RSP_TIMEOUT_VALUE = 5000; 115 // NOTE: the value is not "final" - it is modified in the unit tests 116 @VisibleForTesting static int sConnectTimeoutMillis = 30000; 117 118 private BluetoothDevice mCurrentDevice; 119 120 // State machine states 121 private final Disconnected mDisconnected = new Disconnected(); 122 private final Connecting mConnecting = new Connecting(); 123 private final Disconnecting mDisconnecting = new Disconnecting(); 124 private final Connected mConnected = new Connected(); 125 private final AudioOn mAudioOn = new AudioOn(); 126 private final AudioConnecting mAudioConnecting = new AudioConnecting(); 127 private final AudioDisconnecting mAudioDisconnecting = new AudioDisconnecting(); 128 private HeadsetStateBase mPrevState; 129 130 // Run time dependencies 131 private final HeadsetService mService; 132 private final HeadsetNativeInterface mNativeInterface; 133 private final HeadsetSystemInterface mSystemInterface; 134 private final BluetoothAdapter mAdapter; 135 136 // Runtime states 137 private boolean mVirtualCallStarted; 138 private boolean mVoiceRecognitionStarted; 139 private boolean mWaitingForVoiceRecognition; 140 private boolean mDialingOut; 141 private int mSpeakerVolume; 142 private int mMicVolume; 143 // Indicates whether audio can be routed to the device. 144 private boolean mAudioRouteAllowed = true; 145 // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions 146 private boolean mForceScoAudio; 147 // Audio Parameters like NREC 148 private final HashMap<String, Integer> mAudioParams = new HashMap<>(); 149 // AT Phone book keeps a group of states used by AT+CPBR commands 150 private final AtPhonebook mPhonebook; 151 152 private static final ParcelUuid[] HEADSET_UUIDS = { 153 BluetoothUuid.HSP, BluetoothUuid.Handsfree, 154 }; 155 // Keys are AT commands, and values are the company IDs. 156 private static final Map<String, Integer> VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID; 157 // Intent that get sent during voice recognition events. 158 private static final Intent VOICE_COMMAND_INTENT; 159 160 static { 161 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap<>(); 162 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 163 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT, 164 BluetoothAssignedNumbers.PLANTRONICS); 165 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 166 BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID, 167 BluetoothAssignedNumbers.GOOGLE); 168 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 169 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XAPL, 170 BluetoothAssignedNumbers.APPLE); 171 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 172 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV, 173 BluetoothAssignedNumbers.APPLE); 174 VOICE_COMMAND_INTENT = new Intent(Intent.ACTION_VOICE_COMMAND); 175 VOICE_COMMAND_INTENT.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 176 } 177 178 private HeadsetStateMachine(Looper looper, HeadsetService service, 179 HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { 180 super(TAG, looper); 181 // Enable/Disable StateMachine debug logs 182 setDbg(DBG); 183 mService = service; 184 mNativeInterface = nativeInterface; 185 mSystemInterface = systemInterface; 186 187 // Connect to system services and construct helper objects 188 mAdapter = BluetoothAdapter.getDefaultAdapter(); 189 mPhonebook = new AtPhonebook(mService, mNativeInterface); 190 191 // Initialize state machine 192 addState(mDisconnected); 193 addState(mConnecting); 194 addState(mDisconnecting); 195 addState(mConnected); 196 addState(mAudioOn); 197 addState(mAudioConnecting); 198 addState(mAudioDisconnecting); 199 setInitialState(mDisconnected); 200 } 201 202 static HeadsetStateMachine make(Looper looper, HeadsetService service, 203 HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { 204 Log.i(TAG, "make"); 205 HeadsetStateMachine stateMachine = 206 new HeadsetStateMachine(looper, service, nativeInterface, systemInterface); 207 stateMachine.start(); 208 return stateMachine; 209 } 210 211 static void destroy(HeadsetStateMachine stateMachine) { 212 Log.i(TAG, "destroy"); 213 if (stateMachine == null) { 214 Log.w(TAG, "destroy(), stateMachine is null"); 215 return; 216 } 217 stateMachine.quitNow(); 218 stateMachine.cleanup(); 219 } 220 221 public void cleanup() { 222 if (mPhonebook != null) { 223 mPhonebook.cleanup(); 224 } 225 mAudioParams.clear(); 226 } 227 228 public void dump(StringBuilder sb) { 229 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice); 230 ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted); 231 ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 232 ProfileService.println(sb, "mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition); 233 ProfileService.println(sb, "mForceScoAudio: " + mForceScoAudio); 234 ProfileService.println(sb, "mDialingOut: " + mDialingOut); 235 ProfileService.println(sb, "mAudioRouteAllowed: " + mAudioRouteAllowed); 236 ProfileService.println(sb, "StateMachine: " + this); 237 ProfileService.println(sb, "PreviousState: " + mPrevState); 238 ProfileService.println(sb, "mAudioState: " + getAudioState()); 239 // Dump the state machine logs 240 StringWriter stringWriter = new StringWriter(); 241 PrintWriter printWriter = new PrintWriter(stringWriter); 242 super.dump(new FileDescriptor(), printWriter, new String[]{}); 243 printWriter.flush(); 244 stringWriter.flush(); 245 ProfileService.println(sb, "StateMachineLog: " + stringWriter.toString()); 246 } 247 248 /** 249 * Base class for states used in this state machine to share common infrastructures 250 */ 251 private abstract class HeadsetStateBase extends State { 252 @Override 253 public void enter() { 254 // Crash if current device is null and state is not Disconnected 255 if (!(this instanceof Disconnected) && mCurrentDevice == null) { 256 throw new IllegalStateException("mCurrentDevice is null on enter()"); 257 } 258 // Crash if mPrevState is null and state is not Disconnected 259 if (!(this instanceof Disconnected) && mPrevState == null) { 260 throw new IllegalStateException("mPrevState is null on enter()"); 261 } 262 enforceValidConnectionStateTransition(); 263 } 264 265 @Override 266 public void exit() { 267 Message message = getCurrentMessage(); 268 if (message != null && !isQuit(message) && mCurrentDevice == null) { 269 throw new IllegalStateException( 270 "mCurrentDevice is null on exit() to non-quitting state"); 271 } 272 mPrevState = this; 273 } 274 275 @Override 276 public String toString() { 277 return getName(); 278 } 279 280 /** 281 * Broadcast audio and connection state changes to the system. This should be called at the 282 * end of enter() method after all the setup is done 283 */ 284 void broadcastStateTransitions() { 285 if (mPrevState == null || mCurrentDevice == null) { 286 return; 287 } 288 // TODO: Add STATE_AUDIO_DISCONNECTING constant to get rid of the 2nd part of this logic 289 if (getAudioStateInt() != mPrevState.getAudioStateInt() || ( 290 mPrevState instanceof AudioDisconnecting && this instanceof AudioOn)) { 291 stateLogD("audio state changed: " + mCurrentDevice + ": " + mPrevState + " -> " 292 + this); 293 broadcastAudioState(mCurrentDevice, mPrevState.getAudioStateInt(), 294 getAudioStateInt()); 295 } 296 if (getConnectionStateInt() != mPrevState.getConnectionStateInt()) { 297 stateLogD("connection state changed: " + mCurrentDevice + ": " + mPrevState + " -> " 298 + this); 299 broadcastConnectionState(mCurrentDevice, mPrevState.getConnectionStateInt(), 300 getConnectionStateInt()); 301 } 302 } 303 304 // Should not be called from enter() method 305 void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) { 306 stateLogD("broadcastConnectionState " + device + ": " + fromState + "->" + toState); 307 if (fromState == BluetoothProfile.STATE_CONNECTED) { 308 // Headset is disconnecting, stop Virtual call if active. 309 terminateScoUsingVirtualVoiceCall(); 310 } 311 mService.connectionStateChanged(device, fromState, toState); 312 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 313 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); 314 intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); 315 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 316 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 317 mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 318 } 319 320 // Should not be called from enter() method 321 void broadcastAudioState(BluetoothDevice device, int fromState, int toState) { 322 stateLogD("broadcastAudioState: " + device + ": " + fromState + "->" + toState); 323 if (fromState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 324 // When SCO gets disconnected during call transfer, Virtual call 325 // needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall. 326 terminateScoUsingVirtualVoiceCall(); 327 } 328 Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 329 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); 330 intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); 331 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 332 mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 333 } 334 335 /** 336 * Verify if the current state transition is legal. This is supposed to be called from 337 * enter() method and crash if the state transition is out of the specification 338 * 339 * Note: 340 * This method uses state objects to verify transition because these objects should be final 341 * and any other instances are invalid 342 */ 343 void enforceValidConnectionStateTransition() { 344 boolean result = false; 345 if (this == mDisconnected) { 346 result = mPrevState == null || mPrevState == mConnecting 347 || mPrevState == mDisconnecting 348 // TODO: edges to be removed after native stack refactoring 349 // all transitions to disconnected state should go through a pending state 350 // also, states should not go directly from an active audio state to 351 // disconnected state 352 || mPrevState == mConnected || mPrevState == mAudioOn 353 || mPrevState == mAudioConnecting || mPrevState == mAudioDisconnecting; 354 } else if (this == mConnecting) { 355 result = mPrevState == mDisconnected; 356 } else if (this == mDisconnecting) { 357 result = mPrevState == mConnected 358 // TODO: edges to be removed after native stack refactoring 359 // all transitions to disconnecting state should go through connected state 360 || mPrevState == mAudioConnecting || mPrevState == mAudioOn 361 || mPrevState == mAudioDisconnecting; 362 } else if (this == mConnected) { 363 result = mPrevState == mConnecting || mPrevState == mAudioDisconnecting 364 || mPrevState == mDisconnecting || mPrevState == mAudioConnecting 365 // TODO: edges to be removed after native stack refactoring 366 // all transitions to connected state should go through a pending state 367 || mPrevState == mAudioOn || mPrevState == mDisconnected; 368 } else if (this == mAudioConnecting) { 369 result = mPrevState == mConnected; 370 } else if (this == mAudioDisconnecting) { 371 result = mPrevState == mAudioOn; 372 } else if (this == mAudioOn) { 373 result = mPrevState == mAudioConnecting || mPrevState == mAudioDisconnecting 374 // TODO: edges to be removed after native stack refactoring 375 // all transitions to audio connected state should go through a pending 376 // state 377 || mPrevState == mConnected; 378 } 379 if (!result) { 380 throw new IllegalStateException( 381 "Invalid state transition from " + mPrevState + " to " + this 382 + " for device " + mCurrentDevice); 383 } 384 } 385 386 void stateLogD(String msg) { 387 log(getName() + ": " + msg); 388 } 389 390 void stateLogW(String msg) { 391 logw(getName() + ": " + msg); 392 } 393 394 void stateLogE(String msg) { 395 loge(getName() + ": " + msg); 396 } 397 398 void stateLogV(String msg) { 399 logv(getName() + ": " + msg); 400 } 401 402 void stateLogI(String msg) { 403 logi(getName() + ": " + msg); 404 } 405 406 void stateLogWtfStack(String msg) { 407 Log.wtfStack(TAG, getName() + ": " + msg); 408 } 409 410 /** 411 * Process connection event 412 * 413 * @param message the current message for the event 414 * @param state connection state to transition to 415 * @param device associated device 416 */ 417 public abstract void processConnectionEvent(Message message, int state, 418 BluetoothDevice device); 419 420 /** 421 * Get a state value from {@link BluetoothProfile} that represents the connection state of 422 * this headset state 423 * 424 * @return a value in {@link BluetoothProfile#STATE_DISCONNECTED}, 425 * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or 426 * {@link BluetoothProfile#STATE_DISCONNECTING} 427 */ 428 abstract int getConnectionStateInt(); 429 430 /** 431 * Get an audio state value from {@link BluetoothHeadset} 432 * @return a value in {@link BluetoothHeadset#STATE_AUDIO_DISCONNECTED}, 433 * {@link BluetoothHeadset#STATE_AUDIO_CONNECTING}, or 434 * {@link BluetoothHeadset#STATE_AUDIO_CONNECTED} 435 */ 436 abstract int getAudioStateInt(); 437 438 } 439 440 class Disconnected extends HeadsetStateBase { 441 @Override 442 int getConnectionStateInt() { 443 return BluetoothProfile.STATE_DISCONNECTED; 444 } 445 446 @Override 447 int getAudioStateInt() { 448 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 449 } 450 451 @Override 452 public void enter() { 453 super.enter(); 454 mPhonebook.resetAtState(); 455 mSystemInterface.getHeadsetPhoneState().listenForPhoneState(false); 456 mVoiceRecognitionStarted = false; 457 mWaitingForVoiceRecognition = false; 458 mAudioParams.clear(); 459 processWBSEvent(HeadsetHalConstants.BTHF_WBS_NO); 460 broadcastStateTransitions(); 461 mCurrentDevice = null; 462 } 463 464 @Override 465 public boolean processMessage(Message message) { 466 if (mCurrentDevice != null) { 467 stateLogE("mCurrentDevice is not null"); 468 return NOT_HANDLED; 469 } 470 switch (message.what) { 471 case CONNECT: 472 BluetoothDevice device = (BluetoothDevice) message.obj; 473 stateLogD("Connecting to " + device); 474 if (!mNativeInterface.connectHfp(device)) { 475 // No state transition is involved, fire broadcast immediately 476 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 477 BluetoothProfile.STATE_DISCONNECTED); 478 break; 479 } 480 mCurrentDevice = device; 481 transitionTo(mConnecting); 482 break; 483 case DISCONNECT: 484 // ignore 485 break; 486 case CALL_STATE_CHANGED: 487 processCallState((HeadsetCallState) message.obj, message.arg1 == 1); 488 break; 489 case DEVICE_STATE_CHANGED: 490 stateLogD("Ignoring DEVICE_STATE_CHANGED event"); 491 break; 492 case STACK_EVENT: 493 HeadsetStackEvent event = (HeadsetStackEvent) message.obj; 494 stateLogD("STACK_EVENT: " + event); 495 switch (event.type) { 496 case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 497 processConnectionEvent(message, event.valueInt, event.device); 498 break; 499 default: 500 stateLogE("Unexpected stack event: " + event); 501 break; 502 } 503 break; 504 default: 505 stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message); 506 return NOT_HANDLED; 507 } 508 return HANDLED; 509 } 510 511 // in Disconnected state 512 @Override 513 public void processConnectionEvent(Message message, int state, BluetoothDevice device) { 514 stateLogD("processConnectionEvent, state=" + state + ", device=" + device); 515 switch (state) { 516 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 517 stateLogW("ignore DISCONNECTED event, device=" + device); 518 break; 519 // Both events result in Connecting state as SLC establishment is still required 520 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 521 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 522 if (okToAcceptConnection(device)) { 523 stateLogI("connected/connecting incoming HF, device=" + device); 524 mCurrentDevice = device; 525 transitionTo(mConnecting); 526 } else { 527 stateLogI("rejected incoming HF, priority=" + mService.getPriority(device) 528 + " bondState=" + device.getBondState() + ", device=" + device); 529 // Reject the connection and stay in Disconnected state itself 530 if (!mNativeInterface.disconnectHfp(device)) { 531 stateLogE("Failed to disconnect from " + device); 532 } 533 // Indicate rejection to other components. 534 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 535 BluetoothProfile.STATE_DISCONNECTED); 536 } 537 break; 538 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 539 stateLogW("Ignore DISCONNECTING event, device=" + device); 540 break; 541 default: 542 stateLogE("Incorrect state: " + state); 543 break; 544 } 545 } 546 } 547 548 // Per HFP 1.7.1 spec page 23/144, Pending state needs to handle 549 // AT+BRSF, AT+CIND, AT+CMER, AT+BIND, +CHLD 550 // commands during SLC establishment 551 class Connecting extends HeadsetStateBase { 552 @Override 553 int getConnectionStateInt() { 554 return BluetoothProfile.STATE_CONNECTING; 555 } 556 557 @Override 558 int getAudioStateInt() { 559 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 560 } 561 562 @Override 563 public void enter() { 564 super.enter(); 565 sendMessageDelayed(CONNECT_TIMEOUT, mCurrentDevice, sConnectTimeoutMillis); 566 broadcastStateTransitions(); 567 } 568 569 @Override 570 public boolean processMessage(Message message) { 571 switch (message.what) { 572 case CONNECT: 573 case CONNECT_AUDIO: 574 case DISCONNECT: 575 deferMessage(message); 576 break; 577 case CONNECT_TIMEOUT: 578 // We timed out trying to connect, transition to Disconnected state 579 stateLogW("Connection timeout for " + mCurrentDevice); 580 transitionTo(mDisconnected); 581 break; 582 case CALL_STATE_CHANGED: 583 processCallState((HeadsetCallState) message.obj, message.arg1 == 1); 584 break; 585 case DEVICE_STATE_CHANGED: 586 stateLogD("ignoring DEVICE_STATE_CHANGED event"); 587 break; 588 case STACK_EVENT: 589 HeadsetStackEvent event = (HeadsetStackEvent) message.obj; 590 stateLogD("STACK_EVENT: " + event); 591 switch (event.type) { 592 case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 593 processConnectionEvent(message, event.valueInt, event.device); 594 break; 595 case HeadsetStackEvent.EVENT_TYPE_AT_CHLD: 596 processAtChld(event.valueInt, event.device); 597 break; 598 case HeadsetStackEvent.EVENT_TYPE_AT_CIND: 599 processAtCind(event.device); 600 break; 601 case HeadsetStackEvent.EVENT_TYPE_WBS: 602 processWBSEvent(event.valueInt); 603 break; 604 case HeadsetStackEvent.EVENT_TYPE_BIND: 605 processAtBind(event.valueString, event.device); 606 break; 607 // Unexpected AT commands, we only handle them for comparability reasons 608 case HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED: 609 stateLogW("Unexpected VR event, device=" + event.device + ", state=" 610 + event.valueInt); 611 processVrEvent(event.valueInt, event.device); 612 break; 613 case HeadsetStackEvent.EVENT_TYPE_DIAL_CALL: 614 stateLogW("Unexpected dial event, device=" + event.device); 615 processDialCall(event.valueString, event.device); 616 break; 617 case HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 618 stateLogW("Unexpected subscriber number event for" + event.device 619 + ", state=" + event.valueInt); 620 processSubscriberNumberRequest(event.device); 621 break; 622 case HeadsetStackEvent.EVENT_TYPE_AT_COPS: 623 stateLogW("Unexpected COPS event for " + event.device); 624 processAtCops(event.device); 625 break; 626 case HeadsetStackEvent.EVENT_TYPE_AT_CLCC: 627 Log.w(TAG, "Connecting: Unexpected CLCC event for" + event.device); 628 processAtClcc(event.device); 629 break; 630 case HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT: 631 stateLogW("Unexpected unknown AT event for" + event.device + ", cmd=" 632 + event.valueString); 633 processUnknownAt(event.valueString, event.device); 634 break; 635 case HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED: 636 stateLogW("Unexpected key-press event for " + event.device); 637 processKeyPressed(event.device); 638 break; 639 case HeadsetStackEvent.EVENT_TYPE_BIEV: 640 stateLogW("Unexpected BIEV event for " + event.device + ", indId=" 641 + event.valueInt + ", indVal=" + event.valueInt2); 642 processAtBiev(event.valueInt, event.valueInt2, event.device); 643 break; 644 case HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED: 645 stateLogW("Unexpected volume event for " + event.device); 646 processVolumeEvent(event.valueInt, event.valueInt2, event.device); 647 break; 648 case HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL: 649 stateLogW("Unexpected answer event for " + event.device); 650 mSystemInterface.answerCall(event.device); 651 break; 652 case HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL: 653 stateLogW("Unexpected hangup event for " + event.device); 654 mSystemInterface.hangupCall(event.device, isVirtualCallInProgress()); 655 break; 656 default: 657 stateLogE("Unexpected event: " + event); 658 break; 659 } 660 break; 661 default: 662 stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message); 663 return NOT_HANDLED; 664 } 665 return HANDLED; 666 } 667 668 @Override 669 public void processConnectionEvent(Message message, int state, BluetoothDevice device) { 670 stateLogD("processConnectionEvent, state=" + state + ", device=" + device); 671 switch (state) { 672 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 673 if (!mCurrentDevice.equals(device)) { 674 stateLogW("Unknown device disconnected" + device); 675 break; 676 } 677 transitionTo(mDisconnected); 678 break; 679 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 680 stateLogD("RFCOMM connected for " + device); 681 if (!mCurrentDevice.equals(device)) { 682 stateLogW("Reject connection from unknown device " + device); 683 if (!mNativeInterface.disconnectHfp(device)) { 684 stateLogE("Disconnect from " + device + " failed"); 685 } 686 } 687 break; 688 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 689 stateLogD("SLC connected for " + device); 690 if (!mCurrentDevice.equals(device)) { 691 stateLogW("Reject SLC from unknown device " + device); 692 if (!mNativeInterface.disconnectHfp(device)) { 693 stateLogE("Disconnect SLC from " + device + " failed"); 694 } 695 break; 696 } 697 configAudioParameters(device); 698 mSystemInterface.queryPhoneState(); 699 transitionTo(mConnected); 700 break; 701 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 702 // Ignored 703 break; 704 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 705 stateLogD("Disconnecting for " + device); 706 if (mCurrentDevice.equals(device)) { 707 stateLogW("Current device disconnecting"); 708 // ignored, wait for it to be disconnected 709 } 710 break; 711 default: 712 stateLogE("Incorrect state " + state); 713 break; 714 } 715 } 716 717 @Override 718 public void exit() { 719 removeMessages(CONNECT_TIMEOUT); 720 super.exit(); 721 } 722 } 723 724 class Disconnecting extends HeadsetStateBase { 725 @Override 726 int getConnectionStateInt() { 727 return BluetoothProfile.STATE_DISCONNECTING; 728 } 729 730 @Override 731 int getAudioStateInt() { 732 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 733 } 734 735 @Override 736 public void enter() { 737 super.enter(); 738 sendMessageDelayed(CONNECT_TIMEOUT, mCurrentDevice, sConnectTimeoutMillis); 739 broadcastStateTransitions(); 740 } 741 742 @Override 743 public boolean processMessage(Message message) { 744 switch (message.what) { 745 case CONNECT: 746 case CONNECT_AUDIO: 747 case DISCONNECT: 748 deferMessage(message); 749 break; 750 case CONNECT_TIMEOUT: 751 stateLogE("timeout"); 752 transitionTo(mDisconnected); 753 break; 754 case STACK_EVENT: 755 HeadsetStackEvent event = (HeadsetStackEvent) message.obj; 756 stateLogD("STACK_EVENT: " + event); 757 switch (event.type) { 758 case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 759 processConnectionEvent(message, event.valueInt, event.device); 760 break; 761 default: 762 stateLogE("Unexpected event: " + event); 763 break; 764 } 765 break; 766 default: 767 stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message); 768 return NOT_HANDLED; 769 } 770 return HANDLED; 771 } 772 773 // in Disconnecting state 774 @Override 775 public void processConnectionEvent(Message message, int state, BluetoothDevice device) { 776 if (!mCurrentDevice.equals(device)) { 777 stateLogW("processConnectionEvent, unknown device " + device); 778 return; 779 } 780 switch (state) { 781 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 782 stateLogD("Device disconnected, device=" + device); 783 transitionTo(mDisconnected); 784 break; 785 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 786 stateLogD("Device connected, device=" + device); 787 transitionTo(mConnected); 788 break; 789 default: 790 stateLogE("Device: " + device + " bad state: " + state); 791 break; 792 } 793 } 794 795 @Override 796 public void exit() { 797 removeMessages(CONNECT_TIMEOUT); 798 super.exit(); 799 } 800 } 801 802 /** 803 * Base class for Connected, AudioConnecting, AudioOn, AudioDisconnecting states 804 */ 805 private abstract class ConnectedBase extends HeadsetStateBase { 806 @Override 807 int getConnectionStateInt() { 808 return BluetoothProfile.STATE_CONNECTED; 809 } 810 811 /** 812 * Handle common messages in connected states. However, state specific messages must be 813 * handled individually. 814 * 815 * @param message Incoming message to handle 816 * @return True if handled successfully, False otherwise 817 */ 818 @Override 819 public boolean processMessage(Message message) { 820 switch (message.what) { 821 case CONNECT: 822 case DISCONNECT: 823 case CONNECT_AUDIO: 824 case DISCONNECT_AUDIO: 825 case CONNECT_TIMEOUT: 826 stateLogWtfStack("Illegal message in generic handler: " + message); 827 break; 828 case VOICE_RECOGNITION_START: 829 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED, 830 (BluetoothDevice) message.obj); 831 break; 832 case VOICE_RECOGNITION_STOP: 833 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED, 834 (BluetoothDevice) message.obj); 835 break; 836 case CALL_STATE_CHANGED: 837 processCallState((HeadsetCallState) message.obj, message.arg1 == 1); 838 break; 839 case DEVICE_STATE_CHANGED: 840 processDeviceStateChanged((HeadsetDeviceState) message.obj); 841 break; 842 case SEND_CCLC_RESPONSE: 843 processSendClccResponse((HeadsetClccResponse) message.obj); 844 break; 845 case CLCC_RSP_TIMEOUT: { 846 BluetoothDevice device = (BluetoothDevice) message.obj; 847 mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0); 848 } 849 break; 850 case SEND_VENDOR_SPECIFIC_RESULT_CODE: 851 processSendVendorSpecificResultCode( 852 (HeadsetVendorSpecificResultCode) message.obj); 853 break; 854 case DIALING_OUT_TIMEOUT: { 855 BluetoothDevice device = (BluetoothDevice) message.obj; 856 if (mDialingOut) { 857 mDialingOut = false; 858 mNativeInterface.atResponseCode(device, 859 HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 860 } 861 } 862 break; 863 case VIRTUAL_CALL_START: 864 initiateScoUsingVirtualVoiceCall(); 865 break; 866 case VIRTUAL_CALL_STOP: 867 terminateScoUsingVirtualVoiceCall(); 868 break; 869 case START_VR_TIMEOUT: { 870 BluetoothDevice device = (BluetoothDevice) message.obj; 871 if (mWaitingForVoiceRecognition) { 872 device = (BluetoothDevice) message.obj; 873 mWaitingForVoiceRecognition = false; 874 stateLogE("Timeout waiting for voice recognition to start"); 875 mNativeInterface.atResponseCode(device, 876 HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 877 } 878 } 879 break; 880 case INTENT_CONNECTION_ACCESS_REPLY: 881 handleAccessPermissionResult((Intent) message.obj); 882 break; 883 case STACK_EVENT: 884 HeadsetStackEvent event = (HeadsetStackEvent) message.obj; 885 stateLogD("STACK_EVENT: " + event); 886 switch (event.type) { 887 case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 888 processConnectionEvent(message, event.valueInt, event.device); 889 break; 890 case HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED: 891 processAudioEvent(event.valueInt, event.device); 892 break; 893 case HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED: 894 processVrEvent(event.valueInt, event.device); 895 break; 896 case HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL: 897 mSystemInterface.answerCall(event.device); 898 break; 899 case HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL: 900 mSystemInterface.hangupCall(event.device, mVirtualCallStarted); 901 break; 902 case HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED: 903 processVolumeEvent(event.valueInt, event.valueInt2, event.device); 904 break; 905 case HeadsetStackEvent.EVENT_TYPE_DIAL_CALL: 906 processDialCall(event.valueString, event.device); 907 break; 908 case HeadsetStackEvent.EVENT_TYPE_SEND_DTMF: 909 mSystemInterface.sendDtmf(event.valueInt, event.device); 910 break; 911 case HeadsetStackEvent.EVENT_TYPE_NOICE_REDUCTION: 912 processNoiseReductionEvent(event.valueInt == 1, event.device); 913 break; 914 case HeadsetStackEvent.EVENT_TYPE_WBS: 915 processWBSEvent(event.valueInt); 916 break; 917 case HeadsetStackEvent.EVENT_TYPE_AT_CHLD: 918 processAtChld(event.valueInt, event.device); 919 break; 920 case HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 921 processSubscriberNumberRequest(event.device); 922 break; 923 case HeadsetStackEvent.EVENT_TYPE_AT_CIND: 924 processAtCind(event.device); 925 break; 926 case HeadsetStackEvent.EVENT_TYPE_AT_COPS: 927 processAtCops(event.device); 928 break; 929 case HeadsetStackEvent.EVENT_TYPE_AT_CLCC: 930 processAtClcc(event.device); 931 break; 932 case HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT: 933 processUnknownAt(event.valueString, event.device); 934 break; 935 case HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED: 936 processKeyPressed(event.device); 937 break; 938 case HeadsetStackEvent.EVENT_TYPE_BIND: 939 processAtBind(event.valueString, event.device); 940 break; 941 case HeadsetStackEvent.EVENT_TYPE_BIEV: 942 processAtBiev(event.valueInt, event.valueInt2, event.device); 943 break; 944 default: 945 stateLogE("Unknown stack event: " + event); 946 break; 947 } 948 break; 949 default: 950 stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message); 951 return NOT_HANDLED; 952 } 953 return HANDLED; 954 } 955 956 @Override 957 public void processConnectionEvent(Message message, int state, BluetoothDevice device) { 958 stateLogD("processConnectionEvent, state=" + state + ", device=" + device); 959 switch (state) { 960 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 961 if (mCurrentDevice.equals(device)) { 962 stateLogE("Same device connect RFCOMM again, should never happen"); 963 break; 964 } 965 // reject the connection and stay in Connected state itself 966 stateLogI("Incoming Hf rejected. priority=" + mService.getPriority(device) 967 + " bondState=" + device.getBondState()); 968 if (!mNativeInterface.disconnectHfp(device)) { 969 stateLogW("Fail to disconnect " + device); 970 break; 971 } 972 break; 973 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 974 // Should have been rejected in CONNECTION_STATE_CONNECTED 975 if (mCurrentDevice.equals(device)) { 976 stateLogE("Same device connected SLC again"); 977 break; 978 } 979 // reject the connection and stay in Connected state itself 980 stateLogI("Incoming Hf SLC rejected. priority=" + mService.getPriority(device) 981 + " bondState=" + device.getBondState()); 982 if (!mNativeInterface.disconnectHfp(device)) { 983 stateLogW("Fail to disconnect " + device); 984 break; 985 } 986 break; 987 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 988 if (!mCurrentDevice.equals(device)) { 989 stateLogW("Unknown device disconnecting, device=" + device); 990 break; 991 } 992 stateLogI("Current device disconnecting " + mCurrentDevice); 993 transitionTo(mDisconnecting); 994 break; 995 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 996 if (!mCurrentDevice.equals(device)) { 997 stateLogW("Unknown device disconnected " + device); 998 break; 999 } 1000 stateLogI("Current device disconnected " + mCurrentDevice); 1001 transitionTo(mDisconnected); 1002 break; 1003 default: 1004 stateLogE("Connection State Device: " + device + " bad state: " + state); 1005 break; 1006 } 1007 } 1008 1009 /** 1010 * Each state should handle audio events differently 1011 * 1012 * @param state audio state 1013 * @param device associated device 1014 */ 1015 public abstract void processAudioEvent(int state, BluetoothDevice device); 1016 } 1017 1018 class Connected extends ConnectedBase { 1019 @Override 1020 int getAudioStateInt() { 1021 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 1022 } 1023 1024 @Override 1025 public void enter() { 1026 super.enter(); 1027 // start phone state listener here so that the CIND response as part of SLC can be 1028 // responded to, correctly. 1029 // listenForPhoneState(boolean) internally handles multiple calls to start listen 1030 mSystemInterface.getHeadsetPhoneState().listenForPhoneState(true); 1031 if (mPrevState == mConnecting) { 1032 // Remove pending connection attempts that were deferred during the pending 1033 // state. This is to prevent auto connect attempts from disconnecting 1034 // devices that previously successfully connected. 1035 removeDeferredMessages(CONNECT); 1036 } 1037 broadcastStateTransitions(); 1038 } 1039 1040 @Override 1041 public boolean processMessage(Message message) { 1042 switch (message.what) { 1043 case CONNECT: { 1044 BluetoothDevice device = (BluetoothDevice) message.obj; 1045 stateLogI("CONNECT, device " + device); 1046 if (mCurrentDevice.equals(device)) { 1047 stateLogW("CONNECT, device " + device + " is already connected"); 1048 break; 1049 } 1050 stateLogD("CONNECT, disconnect current device " + mCurrentDevice); 1051 if (!mNativeInterface.disconnectHfp(mCurrentDevice)) { 1052 stateLogW("CONNECT, Failed to disconnect " + mCurrentDevice); 1053 // broadcast immediately as no state transition is involved 1054 // TODO: to be removed with multi-HFP implementation 1055 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1056 BluetoothProfile.STATE_DISCONNECTED); 1057 break; 1058 } 1059 // Defer connect message to future state 1060 deferMessage(message); 1061 transitionTo(mDisconnecting); 1062 } 1063 break; 1064 case DISCONNECT: { 1065 BluetoothDevice device = (BluetoothDevice) message.obj; 1066 stateLogD("DISCONNECT from device=" + device); 1067 if (!mCurrentDevice.equals(device)) { 1068 stateLogW("DISCONNECT, device " + device + " not connected"); 1069 break; 1070 } 1071 if (!mNativeInterface.disconnectHfp(device)) { 1072 // broadcast immediately as no state transition is involved 1073 stateLogE("DISCONNECT from " + device + " failed"); 1074 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 1075 BluetoothProfile.STATE_CONNECTED); 1076 break; 1077 } 1078 transitionTo(mDisconnecting); 1079 } 1080 break; 1081 case CONNECT_AUDIO: 1082 stateLogD("CONNECT_AUDIO, device=" + mCurrentDevice); 1083 if (!isScoAcceptable()) { 1084 stateLogW("CONNECT_AUDIO No Active/Held call, no call setup, and no " 1085 + "in-band ringing, not allowing SCO, device=" + mCurrentDevice); 1086 break; 1087 } 1088 if (!mNativeInterface.connectAudio(mCurrentDevice)) { 1089 stateLogE("Failed to connect SCO audio for " + mCurrentDevice); 1090 // No state change involved, fire broadcast immediately 1091 broadcastAudioState(mCurrentDevice, 1092 BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 1093 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1094 break; 1095 } 1096 transitionTo(mAudioConnecting); 1097 break; 1098 case DISCONNECT_AUDIO: 1099 stateLogD("DISCONNECT_AUDIO, device=" + mCurrentDevice); 1100 // ignore 1101 break; 1102 default: 1103 return super.processMessage(message); 1104 } 1105 return HANDLED; 1106 } 1107 1108 @Override 1109 public void processAudioEvent(int state, BluetoothDevice device) { 1110 stateLogD("processAudioEvent, state=" + state + ", device=" + device); 1111 if (!mCurrentDevice.equals(device)) { 1112 // Crash if audio is connected for unknown device 1113 stateLogWtfStack("Audio changed on unknown device: " + device); 1114 return; 1115 } 1116 switch (state) { 1117 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 1118 if (!isScoAcceptable()) { 1119 stateLogW("Rejecting incoming audio connection from " + device); 1120 if (!mNativeInterface.disconnectAudio(device)) { 1121 stateLogE("Fail to disconnect audio for " + device); 1122 } 1123 break; 1124 } 1125 stateLogI("Audio connected for " + device); 1126 transitionTo(mAudioOn); 1127 break; 1128 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 1129 if (!isScoAcceptable()) { 1130 stateLogW("Rejecting incoming pending audio connection from " + device); 1131 if (!mNativeInterface.disconnectAudio(device)) { 1132 stateLogE("Fail to disconnect audio for " + device); 1133 } 1134 break; 1135 } 1136 stateLogI("Audio connecting for " + device); 1137 transitionTo(mAudioConnecting); 1138 break; 1139 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1140 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1141 // ignore 1142 break; 1143 default: 1144 stateLogE("Audio State Device: " + device + " bad state: " + state); 1145 break; 1146 } 1147 } 1148 } 1149 1150 class AudioConnecting extends ConnectedBase { 1151 @Override 1152 int getAudioStateInt() { 1153 return BluetoothHeadset.STATE_AUDIO_CONNECTING; 1154 } 1155 1156 @Override 1157 public void enter() { 1158 super.enter(); 1159 sendMessageDelayed(CONNECT_TIMEOUT, mCurrentDevice, sConnectTimeoutMillis); 1160 broadcastStateTransitions(); 1161 } 1162 1163 @Override 1164 public boolean processMessage(Message message) { 1165 switch (message.what) { 1166 case CONNECT: 1167 case DISCONNECT: 1168 case CONNECT_AUDIO: 1169 case DISCONNECT_AUDIO: 1170 deferMessage(message); 1171 break; 1172 case CONNECT_TIMEOUT: { 1173 BluetoothDevice device = (BluetoothDevice) message.obj; 1174 if (!mCurrentDevice.equals(device)) { 1175 stateLogW("CONNECT_TIMEOUT for unknown device " + device); 1176 break; 1177 } 1178 stateLogW("CONNECT_TIMEOUT"); 1179 transitionTo(mConnected); 1180 break; 1181 } 1182 default: 1183 return super.processMessage(message); 1184 } 1185 return HANDLED; 1186 } 1187 1188 @Override 1189 public void processAudioEvent(int state, BluetoothDevice device) { 1190 if (!mCurrentDevice.equals(device)) { 1191 // Crash on unknown device audio state change 1192 stateLogWtfStack("Audio state changed on unknown device: " + device); 1193 return; 1194 } 1195 switch (state) { 1196 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1197 stateLogW("Audio connection failed"); 1198 transitionTo(mConnected); 1199 break; 1200 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 1201 // ignore, already in audio connecting state 1202 break; 1203 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1204 // ignore, there is no BluetoothHeadset.STATE_AUDIO_DISCONNECTING 1205 break; 1206 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 1207 stateLogI("Audio connected for device " + device); 1208 transitionTo(mAudioOn); 1209 break; 1210 default: 1211 stateLogE("Audio State Device: " + device + " bad state: " + state); 1212 break; 1213 } 1214 } 1215 1216 @Override 1217 public void exit() { 1218 removeMessages(CONNECT_TIMEOUT); 1219 super.exit(); 1220 } 1221 } 1222 1223 class AudioOn extends ConnectedBase { 1224 @Override 1225 int getAudioStateInt() { 1226 return BluetoothHeadset.STATE_AUDIO_CONNECTED; 1227 } 1228 1229 @Override 1230 public void enter() { 1231 super.enter(); 1232 removeDeferredMessages(CONNECT_AUDIO); 1233 setAudioParameters(mCurrentDevice); 1234 mSystemInterface.getAudioManager().setBluetoothScoOn(true); 1235 broadcastStateTransitions(); 1236 } 1237 1238 @Override 1239 public boolean processMessage(Message message) { 1240 switch (message.what) { 1241 case CONNECT: { 1242 BluetoothDevice device = (BluetoothDevice) message.obj; 1243 stateLogD("CONNECT, device=" + device); 1244 if (mCurrentDevice.equals(device)) { 1245 stateLogW("CONNECT, device " + device + " is connected"); 1246 break; 1247 } 1248 // When connecting separate device, disconnect the current one first 1249 // Disconnect audio and then disconnect SLC 1250 stateLogD("Disconnecting SCO, device=" + mCurrentDevice); 1251 if (!mNativeInterface.disconnectAudio(mCurrentDevice)) { 1252 stateLogE("Disconnect SCO failed, device=" + mCurrentDevice 1253 + ", abort connection to " + device); 1254 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1255 BluetoothProfile.STATE_DISCONNECTED); 1256 break; 1257 } 1258 deferMessage(message); 1259 transitionTo(mAudioDisconnecting); 1260 break; 1261 } 1262 case DISCONNECT: { 1263 BluetoothDevice device = (BluetoothDevice) message.obj; 1264 stateLogD("DISCONNECT, device=" + device); 1265 if (!mCurrentDevice.equals(device)) { 1266 stateLogW("DISCONNECT, device " + device + " not connected"); 1267 break; 1268 } 1269 // Disconnect BT SCO first 1270 if (!mNativeInterface.disconnectAudio(mCurrentDevice)) { 1271 stateLogW("DISCONNECT failed, device=" + mCurrentDevice); 1272 // if disconnect BT SCO failed, transition to mConnected state to force 1273 // disconnect device 1274 } 1275 deferMessage(obtainMessage(DISCONNECT, mCurrentDevice)); 1276 transitionTo(mConnected); 1277 break; 1278 } 1279 case CONNECT_AUDIO: { 1280 BluetoothDevice device = (BluetoothDevice) message.obj; 1281 if (!mCurrentDevice.equals(device)) { 1282 stateLogW("CONNECT_AUDIO device is not connected " + device); 1283 break; 1284 } 1285 stateLogW("CONNECT_AUDIO device auido is already connected " + device); 1286 break; 1287 } 1288 case DISCONNECT_AUDIO: 1289 if (mNativeInterface.disconnectAudio(mCurrentDevice)) { 1290 stateLogD("DISCONNECT_AUDIO, device=" + mCurrentDevice); 1291 transitionTo(mAudioDisconnecting); 1292 } else { 1293 stateLogW("DISCONNECT_AUDIO failed, device=" + mCurrentDevice); 1294 } 1295 break; 1296 case VOICE_RECOGNITION_START: 1297 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED, 1298 (BluetoothDevice) message.obj); 1299 break; 1300 case VOICE_RECOGNITION_STOP: 1301 processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED, 1302 (BluetoothDevice) message.obj); 1303 break; 1304 case INTENT_SCO_VOLUME_CHANGED: 1305 processIntentScoVolume((Intent) message.obj, mCurrentDevice); 1306 break; 1307 case CALL_STATE_CHANGED: 1308 processCallState((HeadsetCallState) message.obj, message.arg1 == 1); 1309 break; 1310 case DEVICE_STATE_CHANGED: 1311 processDeviceStateChanged((HeadsetDeviceState) message.obj); 1312 break; 1313 case SEND_CCLC_RESPONSE: 1314 processSendClccResponse((HeadsetClccResponse) message.obj); 1315 break; 1316 case CLCC_RSP_TIMEOUT: { 1317 BluetoothDevice device = (BluetoothDevice) message.obj; 1318 mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0); 1319 break; 1320 } 1321 case SEND_VENDOR_SPECIFIC_RESULT_CODE: 1322 processSendVendorSpecificResultCode( 1323 (HeadsetVendorSpecificResultCode) message.obj); 1324 break; 1325 1326 case VIRTUAL_CALL_START: 1327 initiateScoUsingVirtualVoiceCall(); 1328 break; 1329 case VIRTUAL_CALL_STOP: 1330 terminateScoUsingVirtualVoiceCall(); 1331 break; 1332 1333 case DIALING_OUT_TIMEOUT: { 1334 if (mDialingOut) { 1335 BluetoothDevice device = (BluetoothDevice) message.obj; 1336 mDialingOut = false; 1337 mNativeInterface.atResponseCode(device, 1338 HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1339 } 1340 break; 1341 } 1342 case START_VR_TIMEOUT: { 1343 if (mWaitingForVoiceRecognition) { 1344 BluetoothDevice device = (BluetoothDevice) message.obj; 1345 mWaitingForVoiceRecognition = false; 1346 stateLogE("Timeout waiting for voice recognition to start"); 1347 mNativeInterface.atResponseCode(device, 1348 HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1349 } 1350 break; 1351 } 1352 case STACK_EVENT: 1353 HeadsetStackEvent event = (HeadsetStackEvent) message.obj; 1354 stateLogD("STACK_EVENT: " + event); 1355 switch (event.type) { 1356 case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 1357 processConnectionEvent(message, event.valueInt, event.device); 1358 break; 1359 case HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED: 1360 processAudioEvent(event.valueInt, event.device); 1361 break; 1362 case HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED: 1363 processVrEvent(event.valueInt, event.device); 1364 break; 1365 case HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL: 1366 mSystemInterface.answerCall(event.device); 1367 break; 1368 case HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL: 1369 mSystemInterface.hangupCall(event.device, mVirtualCallStarted); 1370 break; 1371 case HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED: 1372 processVolumeEvent(event.valueInt, event.valueInt2, event.device); 1373 break; 1374 case HeadsetStackEvent.EVENT_TYPE_DIAL_CALL: 1375 processDialCall(event.valueString, event.device); 1376 break; 1377 case HeadsetStackEvent.EVENT_TYPE_SEND_DTMF: 1378 mSystemInterface.sendDtmf(event.valueInt, event.device); 1379 break; 1380 case HeadsetStackEvent.EVENT_TYPE_NOICE_REDUCTION: 1381 processNoiseReductionEvent(event.valueInt == 1, event.device); 1382 break; 1383 case HeadsetStackEvent.EVENT_TYPE_AT_CHLD: 1384 processAtChld(event.valueInt, event.device); 1385 break; 1386 case HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 1387 processSubscriberNumberRequest(event.device); 1388 break; 1389 case HeadsetStackEvent.EVENT_TYPE_AT_CIND: 1390 processAtCind(event.device); 1391 break; 1392 case HeadsetStackEvent.EVENT_TYPE_AT_COPS: 1393 processAtCops(event.device); 1394 break; 1395 case HeadsetStackEvent.EVENT_TYPE_AT_CLCC: 1396 processAtClcc(event.device); 1397 break; 1398 case HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT: 1399 processUnknownAt(event.valueString, event.device); 1400 break; 1401 case HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED: 1402 processKeyPressed(event.device); 1403 break; 1404 case HeadsetStackEvent.EVENT_TYPE_BIND: 1405 processAtBind(event.valueString, event.device); 1406 break; 1407 case HeadsetStackEvent.EVENT_TYPE_BIEV: 1408 processAtBiev(event.valueInt, event.valueInt2, event.device); 1409 break; 1410 default: 1411 stateLogE("Unknown stack event: " + event); 1412 break; 1413 } 1414 break; 1415 default: 1416 stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message); 1417 return NOT_HANDLED; 1418 } 1419 return HANDLED; 1420 } 1421 1422 // in AudioOn state 1423 @Override 1424 public void processAudioEvent(int state, BluetoothDevice device) { 1425 if (!mCurrentDevice.equals(device)) { 1426 stateLogE("Audio changed on unknown device: " + device); 1427 return; 1428 } 1429 switch (state) { 1430 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1431 stateLogI("Audio disconnected by remote"); 1432 transitionTo(mConnected); 1433 break; 1434 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1435 stateLogI("Audio being disconnected by remote"); 1436 transitionTo(mAudioDisconnecting); 1437 break; 1438 default: 1439 stateLogE("Audio State Device: " + device + " bad state: " + state); 1440 break; 1441 } 1442 } 1443 1444 private void processIntentScoVolume(Intent intent, BluetoothDevice device) { 1445 int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 1446 if (mSpeakerVolume != volumeValue) { 1447 mSpeakerVolume = volumeValue; 1448 mNativeInterface.setVolume(device, HeadsetHalConstants.VOLUME_TYPE_SPK, 1449 mSpeakerVolume); 1450 } 1451 } 1452 1453 @Override 1454 public void exit() { 1455 mSystemInterface.getAudioManager().setBluetoothScoOn(false); 1456 super.exit(); 1457 } 1458 } 1459 1460 class AudioDisconnecting extends ConnectedBase { 1461 @Override 1462 int getAudioStateInt() { 1463 // TODO: need BluetoothHeadset.STATE_AUDIO_DISCONNECTING 1464 return BluetoothHeadset.STATE_AUDIO_CONNECTED; 1465 } 1466 1467 @Override 1468 public void enter() { 1469 super.enter(); 1470 sendMessageDelayed(CONNECT_TIMEOUT, mCurrentDevice, sConnectTimeoutMillis); 1471 broadcastStateTransitions(); 1472 } 1473 1474 @Override 1475 public boolean processMessage(Message message) { 1476 switch (message.what) { 1477 case CONNECT: 1478 case DISCONNECT: 1479 case CONNECT_AUDIO: 1480 case DISCONNECT_AUDIO: 1481 deferMessage(message); 1482 break; 1483 case CONNECT_TIMEOUT: { 1484 BluetoothDevice device = (BluetoothDevice) message.obj; 1485 if (!mCurrentDevice.equals(device)) { 1486 stateLogW("CONNECT_TIMEOUT for unknown device " + device); 1487 break; 1488 } 1489 stateLogW("CONNECT_TIMEOUT"); 1490 transitionTo(mConnected); 1491 break; 1492 } 1493 default: 1494 return super.processMessage(message); 1495 } 1496 return HANDLED; 1497 } 1498 1499 @Override 1500 public void processAudioEvent(int state, BluetoothDevice device) { 1501 if (!mCurrentDevice.equals(device)) { 1502 // Crash if audio state change happen for unknown device 1503 stateLogWtfStack("Audio changed on unknown device: " + device); 1504 return; 1505 } 1506 switch (state) { 1507 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1508 stateLogI("Audio disconnected for " + device); 1509 transitionTo(mConnected); 1510 break; 1511 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1512 // ignore 1513 break; 1514 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 1515 stateLogW("Audio disconnection failed for " + device); 1516 transitionTo(mAudioOn); 1517 break; 1518 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 1519 // ignore, see if it goes into connected state, otherwise, timeout 1520 break; 1521 default: 1522 stateLogE("Audio State Device: " + device + " bad state: " + state); 1523 break; 1524 } 1525 } 1526 1527 @Override 1528 public void exit() { 1529 removeMessages(CONNECT_TIMEOUT); 1530 super.exit(); 1531 } 1532 } 1533 1534 synchronized BluetoothDevice getCurrentDevice() { 1535 return mCurrentDevice; 1536 } 1537 1538 synchronized int getConnectionState(BluetoothDevice device) { 1539 if (mCurrentDevice == null) { 1540 return BluetoothProfile.STATE_DISCONNECTED; 1541 } 1542 if (!mCurrentDevice.equals(device)) { 1543 return BluetoothProfile.STATE_DISCONNECTED; 1544 } 1545 return ((HeadsetStateBase) getCurrentState()).getConnectionStateInt(); 1546 } 1547 1548 List<BluetoothDevice> getConnectedDevices() { 1549 List<BluetoothDevice> devices = new ArrayList<>(); 1550 synchronized (this) { 1551 if (getCurrentState() instanceof ConnectedBase) { 1552 devices.add(mCurrentDevice); 1553 } 1554 } 1555 return devices; 1556 } 1557 1558 void setAudioRouteAllowed(boolean allowed) { 1559 mAudioRouteAllowed = allowed; 1560 mNativeInterface.setScoAllowed(allowed); 1561 } 1562 1563 boolean getAudioRouteAllowed() { 1564 return mAudioRouteAllowed; 1565 } 1566 1567 void setForceScoAudio(boolean forced) { 1568 mForceScoAudio = forced; 1569 } 1570 1571 synchronized int getAudioState() { 1572 return ((HeadsetStateBase) getCurrentState()).getAudioStateInt(); 1573 } 1574 1575 private void processVrEvent(int state, BluetoothDevice device) { 1576 if (device == null) { 1577 Log.w(TAG, "processVrEvent device is null"); 1578 return; 1579 } 1580 Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " 1581 + mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " 1582 + mWaitingForVoiceRecognition + " isInCall: " + isInCall()); 1583 if (state == HeadsetHalConstants.VR_STATE_STARTED) { 1584 if (!isVirtualCallInProgress() && !isInCall()) { 1585 IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface( 1586 ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); 1587 if (dic != null) { 1588 try { 1589 dic.exitIdle("voice-command"); 1590 } catch (RemoteException e) { 1591 } 1592 } 1593 try { 1594 mService.startActivity(VOICE_COMMAND_INTENT); 1595 } catch (ActivityNotFoundException e) { 1596 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 1597 0); 1598 return; 1599 } 1600 expectVoiceRecognition(device); 1601 } else { 1602 // send error response if call is ongoing 1603 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1604 return; 1605 } 1606 } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) { 1607 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) { 1608 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 1609 mVoiceRecognitionStarted = false; 1610 mWaitingForVoiceRecognition = false; 1611 if (!isInCall() && (getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED)) { 1612 mNativeInterface.disconnectAudio(mCurrentDevice); 1613 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false"); 1614 } 1615 } else { 1616 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1617 } 1618 } else { 1619 Log.e(TAG, "Bad Voice Recognition state: " + state); 1620 } 1621 } 1622 1623 private void processLocalVrEvent(int state, BluetoothDevice device1) { 1624 BluetoothDevice device = null; 1625 if (state == HeadsetHalConstants.VR_STATE_STARTED) { 1626 boolean needAudio = true; 1627 if (mVoiceRecognitionStarted || isInCall()) { 1628 Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() 1629 + " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 1630 return; 1631 } 1632 mVoiceRecognitionStarted = true; 1633 1634 if (mWaitingForVoiceRecognition) { 1635 device = getDeviceForMessage(START_VR_TIMEOUT); 1636 if (device == null) { 1637 return; 1638 } 1639 1640 Log.d(TAG, "Voice recognition started successfully"); 1641 mWaitingForVoiceRecognition = false; 1642 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 1643 removeMessages(START_VR_TIMEOUT); 1644 } else { 1645 Log.d(TAG, "Voice recognition started locally"); 1646 needAudio = mNativeInterface.startVoiceRecognition(mCurrentDevice); 1647 if (mCurrentDevice != null) { 1648 device = mCurrentDevice; 1649 } 1650 } 1651 1652 if (needAudio && getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1653 Log.d(TAG, "Initiating audio connection for Voice Recognition"); 1654 // At this stage, we need to be sure that AVDTP is not streaming. This is needed 1655 // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in 1656 // streaming state while a SCO connection is established. 1657 // This is needed for VoiceDial scenario alone and not for 1658 // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE 1659 // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed. 1660 // Whereas for VoiceDial we want to activate the SCO connection but we are still 1661 // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream 1662 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true"); 1663 if (device != null) { 1664 mNativeInterface.connectAudio(device); 1665 } else { 1666 Log.e(TAG, "device not found for VR"); 1667 } 1668 } 1669 1670 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1671 mSystemInterface.getVoiceRecognitionWakeLock().release(); 1672 } 1673 } else { 1674 Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " 1675 + mVoiceRecognitionStarted + " mWaitingForVoiceRecognition: " 1676 + mWaitingForVoiceRecognition); 1677 if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) { 1678 mVoiceRecognitionStarted = false; 1679 mWaitingForVoiceRecognition = false; 1680 1681 if (mNativeInterface.stopVoiceRecognition(mCurrentDevice) && !isInCall() 1682 && getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1683 mNativeInterface.disconnectAudio(mCurrentDevice); 1684 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false"); 1685 } 1686 } 1687 } 1688 } 1689 1690 private synchronized void expectVoiceRecognition(BluetoothDevice device) { 1691 mWaitingForVoiceRecognition = true; 1692 Message m = obtainMessage(START_VR_TIMEOUT); 1693 m.obj = getMatchingDevice(device); 1694 sendMessageDelayed(m, START_VR_TIMEOUT_VALUE); 1695 1696 if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1697 mSystemInterface.getVoiceRecognitionWakeLock().acquire(START_VR_TIMEOUT_VALUE); 1698 } 1699 } 1700 1701 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1702 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 1703 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 1704 if (bondedDevices == null) { 1705 return deviceList; 1706 } 1707 synchronized (this) { 1708 for (BluetoothDevice device : bondedDevices) { 1709 ParcelUuid[] featureUuids = device.getUuids(); 1710 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 1711 continue; 1712 } 1713 int connectionState = getConnectionState(device); 1714 for (int state : states) { 1715 if (connectionState == state) { 1716 deviceList.add(device); 1717 } 1718 } 1719 } 1720 } 1721 return deviceList; 1722 } 1723 1724 private BluetoothDevice getDeviceForMessage(int what) { 1725 if (what == CONNECT_TIMEOUT) { 1726 log("getDeviceForMessage: returning mTargetDevice for what=" + what); 1727 return mCurrentDevice; 1728 } 1729 if (mCurrentDevice == null) { 1730 log("getDeviceForMessage: No connected device. what=" + what); 1731 return null; 1732 } 1733 if (getHandler().hasMessages(what, mCurrentDevice)) { 1734 log("getDeviceForMessage: returning " + mCurrentDevice); 1735 return mCurrentDevice; 1736 } 1737 log("getDeviceForMessage: No matching device for " + what + ". Returning null"); 1738 return null; 1739 } 1740 1741 private BluetoothDevice getMatchingDevice(BluetoothDevice device) { 1742 if (mCurrentDevice.equals(device)) { 1743 return mCurrentDevice; 1744 } 1745 return null; 1746 } 1747 1748 /* 1749 * Put the AT command, company ID, arguments, and device in an Intent and broadcast it. 1750 */ 1751 private void broadcastVendorSpecificEventIntent(String command, int companyId, int commandType, 1752 Object[] arguments, BluetoothDevice device) { 1753 log("broadcastVendorSpecificEventIntent(" + command + ")"); 1754 Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT); 1755 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command); 1756 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType); 1757 // assert: all elements of args are Serializable 1758 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments); 1759 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1760 1761 intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." 1762 + Integer.toString(companyId)); 1763 1764 mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 1765 } 1766 1767 private void configAudioParameters(BluetoothDevice device) { 1768 // Reset NREC on connect event. Headset will override later 1769 mAudioParams.put("NREC", 1); 1770 mSystemInterface.getAudioManager() 1771 .setParameters( 1772 HEADSET_NAME + "=" + getCurrentDeviceName(device) + ";" + HEADSET_NREC 1773 + "=on"); 1774 Log.d(TAG, 1775 "configAudioParameters for device:" + device + " are: nrec = " + mAudioParams.get( 1776 "NREC")); 1777 } 1778 1779 private void setAudioParameters(BluetoothDevice device) { 1780 // 1. update nrec value 1781 // 2. update headset name 1782 int mNrec = 0; 1783 if (!mAudioParams.isEmpty()) { 1784 mNrec = mAudioParams.get("NREC"); 1785 } else { 1786 Log.e(TAG, "setAudioParameters: audioParam not found"); 1787 } 1788 1789 if (mNrec == 1) { 1790 Log.d(TAG, "Set NREC: 1 for device:" + device); 1791 mSystemInterface.getAudioManager().setParameters(HEADSET_NREC + "=on"); 1792 } else { 1793 Log.d(TAG, "Set NREC: 0 for device:" + device); 1794 mSystemInterface.getAudioManager().setParameters(HEADSET_NREC + "=off"); 1795 } 1796 mSystemInterface.getAudioManager() 1797 .setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device)); 1798 } 1799 1800 private String parseUnknownAt(String atString) { 1801 StringBuilder atCommand = new StringBuilder(atString.length()); 1802 String result = null; 1803 1804 for (int i = 0; i < atString.length(); i++) { 1805 char c = atString.charAt(i); 1806 if (c == '"') { 1807 int j = atString.indexOf('"', i + 1); // search for closing " 1808 if (j == -1) { // unmatched ", insert one. 1809 atCommand.append(atString.substring(i, atString.length())); 1810 atCommand.append('"'); 1811 break; 1812 } 1813 atCommand.append(atString.substring(i, j + 1)); 1814 i = j; 1815 } else if (c != ' ') { 1816 atCommand.append(Character.toUpperCase(c)); 1817 } 1818 } 1819 result = atCommand.toString(); 1820 return result; 1821 } 1822 1823 private int getAtCommandType(String atCommand) { 1824 int commandType = AtPhonebook.TYPE_UNKNOWN; 1825 String atString = null; 1826 atCommand = atCommand.trim(); 1827 if (atCommand.length() > 5) { 1828 atString = atCommand.substring(5); 1829 if (atString.startsWith("?")) { // Read 1830 commandType = AtPhonebook.TYPE_READ; 1831 } else if (atString.startsWith("=?")) { // Test 1832 commandType = AtPhonebook.TYPE_TEST; 1833 } else if (atString.startsWith("=")) { // Set 1834 commandType = AtPhonebook.TYPE_SET; 1835 } else { 1836 commandType = AtPhonebook.TYPE_UNKNOWN; 1837 } 1838 } 1839 return commandType; 1840 } 1841 1842 /* Method to check if Virtual Call in Progress */ 1843 private boolean isVirtualCallInProgress() { 1844 return mVirtualCallStarted; 1845 } 1846 1847 private void setVirtualCallInProgress(boolean state) { 1848 mVirtualCallStarted = state; 1849 } 1850 1851 /* NOTE: Currently the VirtualCall API does not support handling of 1852 call transfers. If it is initiated from the handsfree device, 1853 HeadsetStateMachine will end the virtual call by calling 1854 terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */ 1855 private synchronized boolean initiateScoUsingVirtualVoiceCall() { 1856 log("initiateScoUsingVirtualVoiceCall: Received"); 1857 // 1. Check if the SCO state is idle 1858 if (isInCall() || mVoiceRecognitionStarted) { 1859 Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress."); 1860 return false; 1861 } 1862 1863 // 2. Send virtual phone state changed to initialize SCO 1864 processCallState(new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0), 1865 true); 1866 processCallState(new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), 1867 true); 1868 processCallState(new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0), 1869 true); 1870 setVirtualCallInProgress(true); 1871 // Done 1872 log("initiateScoUsingVirtualVoiceCall: Done"); 1873 return true; 1874 } 1875 1876 private synchronized boolean terminateScoUsingVirtualVoiceCall() { 1877 log("terminateScoUsingVirtualVoiceCall: Received"); 1878 1879 if (!isVirtualCallInProgress()) { 1880 Log.w(TAG, "terminateScoUsingVirtualVoiceCall: No present call to terminate"); 1881 return false; 1882 } 1883 1884 // 2. Send virtual phone state changed to close SCO 1885 processCallState(new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0), 1886 true); 1887 setVirtualCallInProgress(false); 1888 // Done 1889 log("terminateScoUsingVirtualVoiceCall: Done"); 1890 return true; 1891 } 1892 1893 1894 private void processDialCall(String number, BluetoothDevice device) { 1895 if (device == null) { 1896 Log.w(TAG, "processDialCall device is null"); 1897 return; 1898 } 1899 1900 String dialNumber; 1901 if (mDialingOut) { 1902 log("processDialCall, already dialling"); 1903 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1904 return; 1905 } 1906 if ((number == null) || (number.length() == 0)) { 1907 dialNumber = mPhonebook.getLastDialledNumber(); 1908 if (dialNumber == null) { 1909 log("processDialCall, last dial number null"); 1910 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1911 return; 1912 } 1913 } else if (number.charAt(0) == '>') { 1914 // Yuck - memory dialling requested. 1915 // Just dial last number for now 1916 if (number.startsWith(">9999")) { // for PTS test 1917 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1918 return; 1919 } 1920 log("processDialCall, memory dial do last dial for now"); 1921 dialNumber = mPhonebook.getLastDialledNumber(); 1922 if (dialNumber == null) { 1923 log("processDialCall, last dial number null"); 1924 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1925 return; 1926 } 1927 } else { 1928 // Remove trailing ';' 1929 if (number.charAt(number.length() - 1) == ';') { 1930 number = number.substring(0, number.length() - 1); 1931 } 1932 1933 dialNumber = PhoneNumberUtils.convertPreDial(number); 1934 } 1935 // Check for virtual call to terminate before sending Call Intent 1936 terminateScoUsingVirtualVoiceCall(); 1937 1938 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1939 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 1940 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1941 mService.startActivity(intent); 1942 // TODO(BT) continue send OK reults code after call starts 1943 // hold wait lock, start a timer, set wait call flag 1944 // Get call started indication from bluetooth phone 1945 mDialingOut = true; 1946 Message m = obtainMessage(DIALING_OUT_TIMEOUT); 1947 m.obj = getMatchingDevice(device); 1948 sendMessageDelayed(m, DIALING_OUT_TIMEOUT_VALUE); 1949 } 1950 1951 private void processVolumeEvent(int volumeType, int volume, BluetoothDevice device) { 1952 if (!mCurrentDevice.equals(device)) { 1953 Log.w(TAG, "processVolumeEvent, ignored for unknown device " + device); 1954 return; 1955 } 1956 // When there is an active call, only device in audio focus can change SCO volume 1957 if (mSystemInterface.getHeadsetPhoneState().isInCall() 1958 && getAudioState() != BluetoothHeadset.STATE_AUDIO_CONNECTED) { 1959 Log.w(TAG, "processVolumeEvent, ignored because " + mCurrentDevice 1960 + " does not have audio focus"); 1961 } 1962 if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) { 1963 mSpeakerVolume = volume; 1964 int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0; 1965 mSystemInterface.getAudioManager() 1966 .setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag); 1967 } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) { 1968 // Not used currently 1969 mMicVolume = volume; 1970 } else { 1971 Log.e(TAG, "Bad voluem type: " + volumeType); 1972 } 1973 } 1974 1975 private void processCallState(HeadsetCallState callState, boolean isVirtualCall) { 1976 mSystemInterface.getHeadsetPhoneState().setNumActiveCall(callState.mNumActive); 1977 mSystemInterface.getHeadsetPhoneState().setNumHeldCall(callState.mNumHeld); 1978 mSystemInterface.getHeadsetPhoneState().setCallState(callState.mCallState); 1979 if (mDialingOut) { 1980 if (callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) { 1981 BluetoothDevice device = getDeviceForMessage(DIALING_OUT_TIMEOUT); 1982 if (device == null) { 1983 return; 1984 } 1985 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 1986 removeMessages(DIALING_OUT_TIMEOUT); 1987 } else if (callState.mCallState == HeadsetHalConstants.CALL_STATE_ACTIVE 1988 || callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE) { 1989 mDialingOut = false; 1990 } 1991 } 1992 1993 log("mNumActive: " + callState.mNumActive + " mNumHeld: " + callState.mNumHeld 1994 + " mCallState: " + callState.mCallState); 1995 log("mNumber: " + callState.mNumber + " mType: " + callState.mType); 1996 1997 if (isVirtualCall) { 1998 // virtual call state update 1999 if (getCurrentState() != mDisconnected) { 2000 mNativeInterface.phoneStateChange(callState); 2001 } 2002 } else { 2003 // circuit-switch voice call update 2004 // stop virtual voice call if there is a CSV call ongoing 2005 if (callState.mNumActive > 0 || callState.mNumHeld > 0 2006 || callState.mCallState != HeadsetHalConstants.CALL_STATE_IDLE) { 2007 terminateScoUsingVirtualVoiceCall(); 2008 } 2009 2010 // Specific handling for case of starting MO/MT call while VOIP 2011 // ongoing, terminateScoUsingVirtualVoiceCall() resets callState 2012 // INCOMING/DIALING to IDLE. Some HS send AT+CIND? to read call 2013 // and get wrong value of callsetup. This case is hit only 2014 // SCO for VOIP call is not terminated via SDK API call. 2015 if (mSystemInterface.getHeadsetPhoneState().getCallState() != callState.mCallState) { 2016 mSystemInterface.getHeadsetPhoneState().setCallState(callState.mCallState); 2017 } 2018 2019 // at this step: if there is virtual call ongoing, it means there is no CSV call 2020 // let virtual call continue and skip phone state update 2021 if (!isVirtualCallInProgress()) { 2022 if (getCurrentState() != mDisconnected) { 2023 mNativeInterface.phoneStateChange(callState); 2024 } 2025 } 2026 } 2027 } 2028 2029 private void processNoiseReductionEvent(boolean enable, BluetoothDevice device) { 2030 if (!mAudioParams.isEmpty()) { 2031 if (enable) { 2032 mAudioParams.put("NREC", 1); 2033 } else { 2034 mAudioParams.put("NREC", 0); 2035 } 2036 log("NREC value for device :" + device + " is: " + mAudioParams.get("NREC")); 2037 } else { 2038 Log.e(TAG, "processNoiseReductionEvent: audioParamNrec is null "); 2039 } 2040 2041 if (mCurrentDevice != null && mCurrentDevice.equals(device) 2042 && getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 2043 setAudioParameters(device); 2044 } 2045 } 2046 2047 private void processWBSEvent(int wbsConfig) { 2048 switch (wbsConfig) { 2049 case HeadsetHalConstants.BTHF_WBS_YES: 2050 Log.d(TAG, "AudioManager.setParameters: bt_wbs=on"); 2051 mSystemInterface.getAudioManager().setParameters(HEADSET_WBS + "=on"); 2052 break; 2053 case HeadsetHalConstants.BTHF_WBS_NO: 2054 case HeadsetHalConstants.BTHF_WBS_NONE: 2055 Log.d(TAG, "AudioManager.setParameters: bt_wbs=off, wbsConfig=" + wbsConfig); 2056 mSystemInterface.getAudioManager().setParameters(HEADSET_WBS + "=off"); 2057 break; 2058 default: 2059 Log.e(TAG, "processWBSEvent, unknown wbsConfig " + wbsConfig); 2060 } 2061 } 2062 2063 private void processAtChld(int chld, BluetoothDevice device) { 2064 if (device == null) { 2065 Log.w(TAG, "processAtChld device is null"); 2066 return; 2067 } 2068 if (mSystemInterface.processChld(chld)) { 2069 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 2070 } else { 2071 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2072 } 2073 } 2074 2075 private void processSubscriberNumberRequest(BluetoothDevice device) { 2076 if (device == null) { 2077 Log.w(TAG, "processSubscriberNumberRequest device is null"); 2078 return; 2079 } 2080 String number = mSystemInterface.getSubscriberNumber(); 2081 if (number != null) { 2082 mNativeInterface.atResponseString(device, 2083 "+CNUM: ,\"" + number + "\"," + PhoneNumberUtils.toaFromString(number) + ",,4"); 2084 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 2085 } else { 2086 Log.e(TAG, "getSubscriberNumber returns null"); 2087 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2088 } 2089 } 2090 2091 private void processAtCind(BluetoothDevice device) { 2092 int call, callSetup; 2093 if (device == null) { 2094 Log.w(TAG, "processAtCind device is null"); 2095 return; 2096 } 2097 final HeadsetPhoneState phoneState = mSystemInterface.getHeadsetPhoneState(); 2098 2099 /* Handsfree carkits expect that +CIND is properly responded to 2100 Hence we ensure that a proper response is sent 2101 for the virtual call too.*/ 2102 if (isVirtualCallInProgress()) { 2103 call = 1; 2104 callSetup = 0; 2105 } else { 2106 // regular phone call 2107 call = phoneState.getNumActiveCall(); 2108 callSetup = phoneState.getNumHeldCall(); 2109 } 2110 2111 mNativeInterface.cindResponse(device, phoneState.getCindService(), call, callSetup, 2112 phoneState.getCallState(), phoneState.getCindSignal(), phoneState.getCindRoam(), 2113 phoneState.getCindBatteryCharge()); 2114 } 2115 2116 private void processAtCops(BluetoothDevice device) { 2117 if (device == null) { 2118 Log.w(TAG, "processAtCops device is null"); 2119 return; 2120 } 2121 String operatorName = mSystemInterface.getNetworkOperator(); 2122 if (operatorName == null) { 2123 operatorName = ""; 2124 } 2125 mNativeInterface.copsResponse(device, operatorName); 2126 } 2127 2128 private void processAtClcc(BluetoothDevice device) { 2129 if (device == null) { 2130 Log.w(TAG, "processAtClcc device is null"); 2131 return; 2132 } 2133 if (isVirtualCallInProgress()) { 2134 // In virtual call, send our phone number instead of remote phone number 2135 String phoneNumber = mSystemInterface.getSubscriberNumber(); 2136 if (phoneNumber == null) { 2137 phoneNumber = ""; 2138 } 2139 int type = PhoneNumberUtils.toaFromString(phoneNumber); 2140 mNativeInterface.clccResponse(device, 1, 0, 0, 0, false, phoneNumber, type); 2141 mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0); 2142 } else { 2143 // In Telecom call, ask Telecom to send send remote phone number 2144 if (!mSystemInterface.listCurrentCalls()) { 2145 Log.e(TAG, "processAtClcc: failed to list current calls for " + device); 2146 mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0); 2147 } else { 2148 Message m = obtainMessage(CLCC_RSP_TIMEOUT); 2149 m.obj = getMatchingDevice(device); 2150 sendMessageDelayed(m, CLCC_RSP_TIMEOUT_VALUE); 2151 } 2152 } 2153 } 2154 2155 private void processAtCscs(String atString, int type, BluetoothDevice device) { 2156 log("processAtCscs - atString = " + atString); 2157 if (mPhonebook != null) { 2158 mPhonebook.handleCscsCommand(atString, type, device); 2159 } else { 2160 Log.e(TAG, "Phonebook handle null for At+CSCS"); 2161 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2162 } 2163 } 2164 2165 private void processAtCpbs(String atString, int type, BluetoothDevice device) { 2166 log("processAtCpbs - atString = " + atString); 2167 if (mPhonebook != null) { 2168 mPhonebook.handleCpbsCommand(atString, type, device); 2169 } else { 2170 Log.e(TAG, "Phonebook handle null for At+CPBS"); 2171 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2172 } 2173 } 2174 2175 private void processAtCpbr(String atString, int type, BluetoothDevice device) { 2176 log("processAtCpbr - atString = " + atString); 2177 if (mPhonebook != null) { 2178 mPhonebook.handleCpbrCommand(atString, type, device); 2179 } else { 2180 Log.e(TAG, "Phonebook handle null for At+CPBR"); 2181 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2182 } 2183 } 2184 2185 /** 2186 * Find a character ch, ignoring quoted sections. 2187 * Return input.length() if not found. 2188 */ 2189 private static int findChar(char ch, String input, int fromIndex) { 2190 for (int i = fromIndex; i < input.length(); i++) { 2191 char c = input.charAt(i); 2192 if (c == '"') { 2193 i = input.indexOf('"', i + 1); 2194 if (i == -1) { 2195 return input.length(); 2196 } 2197 } else if (c == ch) { 2198 return i; 2199 } 2200 } 2201 return input.length(); 2202 } 2203 2204 /** 2205 * Break an argument string into individual arguments (comma delimited). 2206 * Integer arguments are turned into Integer objects. Otherwise a String 2207 * object is used. 2208 */ 2209 private static Object[] generateArgs(String input) { 2210 int i = 0; 2211 int j; 2212 ArrayList<Object> out = new ArrayList<Object>(); 2213 while (i <= input.length()) { 2214 j = findChar(',', input, i); 2215 2216 String arg = input.substring(i, j); 2217 try { 2218 out.add(new Integer(arg)); 2219 } catch (NumberFormatException e) { 2220 out.add(arg); 2221 } 2222 2223 i = j + 1; // move past comma 2224 } 2225 return out.toArray(); 2226 } 2227 2228 /** 2229 * Process vendor specific AT commands 2230 * 2231 * @param atString AT command after the "AT+" prefix 2232 * @param device Remote device that has sent this command 2233 */ 2234 private void processVendorSpecificAt(String atString, BluetoothDevice device) { 2235 log("processVendorSpecificAt - atString = " + atString); 2236 2237 // Currently we accept only SET type commands. 2238 int indexOfEqual = atString.indexOf("="); 2239 if (indexOfEqual == -1) { 2240 Log.e(TAG, "processVendorSpecificAt: command type error in " + atString); 2241 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2242 return; 2243 } 2244 2245 String command = atString.substring(0, indexOfEqual); 2246 Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command); 2247 if (companyId == null) { 2248 Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString); 2249 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2250 return; 2251 } 2252 2253 String arg = atString.substring(indexOfEqual + 1); 2254 if (arg.startsWith("?")) { 2255 Log.e(TAG, "processVendorSpecificAt: command type error in " + atString); 2256 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2257 return; 2258 } 2259 2260 Object[] args = generateArgs(arg); 2261 if (command.equals(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XAPL)) { 2262 processAtXapl(args, device); 2263 } 2264 broadcastVendorSpecificEventIntent(command, companyId, BluetoothHeadset.AT_CMD_TYPE_SET, 2265 args, device); 2266 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 2267 } 2268 2269 /** 2270 * Process AT+XAPL AT command 2271 * 2272 * @param args command arguments after the equal sign 2273 * @param device Remote device that has sent this command 2274 */ 2275 private void processAtXapl(Object[] args, BluetoothDevice device) { 2276 if (args.length != 2) { 2277 Log.w(TAG, "processAtXapl() args length must be 2: " + String.valueOf(args.length)); 2278 return; 2279 } 2280 if (!(args[0] instanceof String) || !(args[1] instanceof Integer)) { 2281 Log.w(TAG, "processAtXapl() argument types not match"); 2282 return; 2283 } 2284 // feature = 2 indicates that we support battery level reporting only 2285 mNativeInterface.atResponseString(device, "+XAPL=iPhone," + String.valueOf(2)); 2286 } 2287 2288 private void processUnknownAt(String atString, BluetoothDevice device) { 2289 if (device == null) { 2290 Log.w(TAG, "processUnknownAt device is null"); 2291 return; 2292 } 2293 log("processUnknownAt - atString = " + atString); 2294 String atCommand = parseUnknownAt(atString); 2295 int commandType = getAtCommandType(atCommand); 2296 if (atCommand.startsWith("+CSCS")) { 2297 processAtCscs(atCommand.substring(5), commandType, device); 2298 } else if (atCommand.startsWith("+CPBS")) { 2299 processAtCpbs(atCommand.substring(5), commandType, device); 2300 } else if (atCommand.startsWith("+CPBR")) { 2301 processAtCpbr(atCommand.substring(5), commandType, device); 2302 } else { 2303 processVendorSpecificAt(atCommand, device); 2304 } 2305 } 2306 2307 private void processKeyPressed(BluetoothDevice device) { 2308 if (device == null) { 2309 Log.w(TAG, "processKeyPressed device is null"); 2310 return; 2311 } 2312 final HeadsetPhoneState phoneState = mSystemInterface.getHeadsetPhoneState(); 2313 if (phoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) { 2314 mSystemInterface.answerCall(device); 2315 } else if (phoneState.getNumActiveCall() > 0) { 2316 if (getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 2317 mNativeInterface.connectAudio(mCurrentDevice); 2318 } else { 2319 mSystemInterface.hangupCall(device, false); 2320 } 2321 } else { 2322 String dialNumber = mPhonebook.getLastDialledNumber(); 2323 if (dialNumber == null) { 2324 log("processKeyPressed, last dial number null"); 2325 return; 2326 } 2327 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 2328 Uri.fromParts(SCHEME_TEL, dialNumber, null)); 2329 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2330 mService.startActivity(intent); 2331 } 2332 } 2333 2334 /** 2335 * Send HF indicator value changed intent 2336 * 2337 * @param device Device whose HF indicator value has changed 2338 * @param indId Indicator ID [0-65535] 2339 * @param indValue Indicator Value [0-65535], -1 means invalid but indId is supported 2340 */ 2341 private void sendIndicatorIntent(BluetoothDevice device, int indId, int indValue) { 2342 Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED); 2343 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2344 intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indId); 2345 intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, indValue); 2346 2347 mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); 2348 } 2349 2350 private void processAtBind(String atString, BluetoothDevice device) { 2351 log("processAtBind: " + atString); 2352 2353 // Parse the AT String to find the Indicator Ids that are supported 2354 int indId = 0; 2355 int iter = 0; 2356 int iter1 = 0; 2357 2358 while (iter < atString.length()) { 2359 iter1 = findChar(',', atString, iter); 2360 String id = atString.substring(iter, iter1); 2361 2362 try { 2363 indId = Integer.valueOf(id); 2364 } catch (NumberFormatException e) { 2365 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2366 } 2367 2368 switch (indId) { 2369 case HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY: 2370 log("Send Broadcast intent for the Enhanced Driver Safety indicator."); 2371 sendIndicatorIntent(device, indId, -1); 2372 break; 2373 case HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS: 2374 log("Send Broadcast intent for the Battery Level indicator."); 2375 sendIndicatorIntent(device, indId, -1); 2376 break; 2377 default: 2378 log("Invalid HF Indicator Received"); 2379 break; 2380 } 2381 2382 iter = iter1 + 1; // move past comma 2383 } 2384 } 2385 2386 private void processAtBiev(int indId, int indValue, BluetoothDevice device) { 2387 log("processAtBiev: ind_id=" + indId + ", ind_value=" + indValue); 2388 sendIndicatorIntent(device, indId, indValue); 2389 } 2390 2391 private void processDeviceStateChanged(HeadsetDeviceState deviceState) { 2392 mNativeInterface.notifyDeviceStatus(deviceState); 2393 } 2394 2395 private void processSendClccResponse(HeadsetClccResponse clcc) { 2396 BluetoothDevice device = getDeviceForMessage(CLCC_RSP_TIMEOUT); 2397 if (device == null) { 2398 return; 2399 } 2400 if (clcc.mIndex == 0) { 2401 removeMessages(CLCC_RSP_TIMEOUT); 2402 } 2403 mNativeInterface.clccResponse(device, clcc.mIndex, clcc.mDirection, clcc.mStatus, 2404 clcc.mMode, clcc.mMpty, clcc.mNumber, clcc.mType); 2405 } 2406 2407 private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) { 2408 String stringToSend = resultCode.mCommand + ": "; 2409 if (resultCode.mArg != null) { 2410 stringToSend += resultCode.mArg; 2411 } 2412 mNativeInterface.atResponseString(resultCode.mDevice, stringToSend); 2413 } 2414 2415 private String getCurrentDeviceName(BluetoothDevice device) { 2416 String defaultName = "<unknown>"; 2417 2418 if (device == null) { 2419 return defaultName; 2420 } 2421 2422 String deviceName = device.getName(); 2423 if (deviceName == null) { 2424 return defaultName; 2425 } 2426 return deviceName; 2427 } 2428 2429 private boolean isInCall() { 2430 final HeadsetPhoneState phoneState = mSystemInterface.getHeadsetPhoneState(); 2431 return ((phoneState.getNumActiveCall() > 0) || (phoneState.getNumHeldCall() > 0) || ( 2432 (phoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE) && ( 2433 phoneState.getCallState() != HeadsetHalConstants.CALL_STATE_INCOMING))); 2434 } 2435 2436 private boolean isRinging() { 2437 return mSystemInterface.getHeadsetPhoneState().getCallState() 2438 == HeadsetHalConstants.CALL_STATE_INCOMING; 2439 } 2440 2441 // Accept incoming SCO only when there is in-band ringing, incoming call, 2442 // active call, VR activated, active VOIP call 2443 private boolean isScoAcceptable() { 2444 if (mForceScoAudio) { 2445 return true; 2446 } 2447 if (!mService.getAudioRouteAllowed()) { 2448 return false; 2449 } 2450 if (isInCall() || mVoiceRecognitionStarted) { 2451 return true; 2452 } 2453 if (isRinging() && mService.isInbandRingingEnabled()) { 2454 return true; 2455 } 2456 return false; 2457 } 2458 2459 private boolean okToAcceptConnection(BluetoothDevice device) { 2460 AdapterService adapterService = AdapterService.getAdapterService(); 2461 // check if this is an incoming connection in Quiet mode. 2462 if (adapterService == null) { 2463 Log.e(TAG, "okToAcceptConnection, cannot get adapterService"); 2464 return false; 2465 } 2466 if (adapterService.isQuietModeEnabled() && mCurrentDevice == null) { 2467 Log.i(TAG, "okToAcceptConnection, quiet mode enabled and current device is null"); 2468 return false; 2469 } 2470 // check priority and accept or reject the connection. if priority is undefined 2471 // it is likely that our SDP has not completed and peer is initiating the 2472 // connection. Allow this connection, provided the device is bonded 2473 int priority = mService.getPriority(device); 2474 int bondState = device.getBondState(); 2475 if ((priority > BluetoothProfile.PRIORITY_OFF) || ( 2476 (priority == BluetoothProfile.PRIORITY_UNDEFINED) 2477 && bondState != BluetoothDevice.BOND_NONE)) { 2478 return true; 2479 } 2480 Log.i(TAG, "okToAcceptConnection, rejected, priority=" + priority + ", bondState=" 2481 + bondState); 2482 return false; 2483 } 2484 2485 @Override 2486 protected void log(String msg) { 2487 if (DBG) { 2488 super.log(msg); 2489 } 2490 } 2491 2492 private void handleAccessPermissionResult(Intent intent) { 2493 log("handleAccessPermissionResult"); 2494 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2495 if (!mPhonebook.getCheckingAccessPermission()) { 2496 return; 2497 } 2498 int atCommandResult = 0; 2499 int atCommandErrorCode = 0; 2500 // HeadsetBase headset = mHandsfree.getHeadset(); 2501 // ASSERT: (headset != null) && headSet.isConnected() 2502 // REASON: mCheckingAccessPermission is true, otherwise resetAtState 2503 // has set mCheckingAccessPermission to false 2504 if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 2505 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 2506 BluetoothDevice.CONNECTION_ACCESS_NO) 2507 == BluetoothDevice.CONNECTION_ACCESS_YES) { 2508 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 2509 mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); 2510 } 2511 atCommandResult = mPhonebook.processCpbrCommand(device); 2512 } else { 2513 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 2514 mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); 2515 } 2516 } 2517 } 2518 mPhonebook.setCpbrIndex(-1); 2519 mPhonebook.setCheckingAccessPermission(false); 2520 if (atCommandResult >= 0) { 2521 mNativeInterface.atResponseCode(device, atCommandResult, atCommandErrorCode); 2522 } else { 2523 log("handleAccessPermissionResult - RESULT_NONE"); 2524 } 2525 } 2526 2527 static String getMessageName(int what) { 2528 switch (what) { 2529 case CONNECT: 2530 return "CONNECT"; 2531 case DISCONNECT: 2532 return "DISCONNECT"; 2533 case CONNECT_AUDIO: 2534 return "CONNECT_AUDIO"; 2535 case DISCONNECT_AUDIO: 2536 return "DISCONNECT_AUDIO"; 2537 case VOICE_RECOGNITION_START: 2538 return "VOICE_RECOGNITION_START"; 2539 case VOICE_RECOGNITION_STOP: 2540 return "VOICE_RECOGNITION_STOP"; 2541 case INTENT_SCO_VOLUME_CHANGED: 2542 return "INTENT_SCO_VOLUME_CHANGED"; 2543 case INTENT_CONNECTION_ACCESS_REPLY: 2544 return "INTENT_CONNECTION_ACCESS_REPLY"; 2545 case CALL_STATE_CHANGED: 2546 return "CALL_STATE_CHANGED"; 2547 case DEVICE_STATE_CHANGED: 2548 return "DEVICE_STATE_CHANGED"; 2549 case SEND_CCLC_RESPONSE: 2550 return "SEND_CCLC_RESPONSE"; 2551 case SEND_VENDOR_SPECIFIC_RESULT_CODE: 2552 return "SEND_VENDOR_SPECIFIC_RESULT_CODE"; 2553 case VIRTUAL_CALL_START: 2554 return "VIRTUAL_CALL_START"; 2555 case VIRTUAL_CALL_STOP: 2556 return "VIRTUAL_CALL_STOP"; 2557 case STACK_EVENT: 2558 return "STACK_EVENT"; 2559 case DIALING_OUT_TIMEOUT: 2560 return "DIALING_OUT_TIMEOUT"; 2561 case START_VR_TIMEOUT: 2562 return "START_VR_TIMEOUT"; 2563 case CLCC_RSP_TIMEOUT: 2564 return "CLCC_RSP_TIMEOUT"; 2565 case CONNECT_TIMEOUT: 2566 return "CONNECT_TIMEOUT"; 2567 default: 2568 return "UNKNOWN"; 2569 } 2570 } 2571} 2572