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 package com.android.bluetooth.hfp; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 21 import static com.android.modules.utils.build.SdkLevel.isAtLeastU; 22 23 import static java.util.Objects.requireNonNull; 24 25 import android.annotation.RequiresPermission; 26 import android.bluetooth.BluetoothAdapter; 27 import android.bluetooth.BluetoothAssignedNumbers; 28 import android.bluetooth.BluetoothDevice; 29 import android.bluetooth.BluetoothHeadset; 30 import android.bluetooth.BluetoothProfile; 31 import android.bluetooth.BluetoothProtoEnums; 32 import android.bluetooth.BluetoothSinkAudioPolicy; 33 import android.bluetooth.BluetoothStatusCodes; 34 import android.bluetooth.hfp.BluetoothHfpProtoEnums; 35 import android.content.Intent; 36 import android.media.AudioManager; 37 import android.os.Build; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.SystemClock; 41 import android.os.UserHandle; 42 import android.telephony.PhoneNumberUtils; 43 import android.telephony.PhoneStateListener; 44 import android.telephony.ServiceState; 45 import android.text.TextUtils; 46 import android.util.Log; 47 48 import com.android.bluetooth.BluetoothStatsLog; 49 import com.android.bluetooth.Utils; 50 import com.android.bluetooth.btservice.AdapterService; 51 import com.android.bluetooth.btservice.MetricsLogger; 52 import com.android.bluetooth.btservice.ProfileService; 53 import com.android.bluetooth.btservice.storage.DatabaseManager; 54 import com.android.bluetooth.flags.Flags; 55 import com.android.internal.annotations.VisibleForTesting; 56 import com.android.internal.util.State; 57 import com.android.internal.util.StateMachine; 58 import com.android.modules.expresslog.Counter; 59 60 import java.io.FileDescriptor; 61 import java.io.PrintWriter; 62 import java.io.StringWriter; 63 import java.util.ArrayList; 64 import java.util.HashMap; 65 import java.util.Map; 66 import java.util.Objects; 67 import java.util.Scanner; 68 69 /** 70 * A Bluetooth Handset StateMachine (Disconnected) | ^ CONNECT | | DISCONNECTED V | (Connecting) 71 * (Disconnecting) | ^ CONNECTED | | DISCONNECT V | (Connected) | ^ CONNECT_AUDIO | | 72 * AUDIO_DISCONNECTED V | (AudioConnecting) (AudioDiconnecting) | ^ AUDIO_CONNECTED | | 73 * DISCONNECT_AUDIO V | (AudioOn) 74 */ 75 class HeadsetStateMachine extends StateMachine { 76 private static final String TAG = HeadsetStateMachine.class.getSimpleName(); 77 78 static final int CONNECT = 1; 79 static final int DISCONNECT = 2; 80 static final int CONNECT_AUDIO = 3; 81 static final int DISCONNECT_AUDIO = 4; 82 static final int VOICE_RECOGNITION_START = 5; 83 static final int VOICE_RECOGNITION_STOP = 6; 84 85 // message.obj is an intent AudioManager.ACTION_VOLUME_CHANGED 86 // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO 87 static final int INTENT_SCO_VOLUME_CHANGED = 7; 88 static final int INTENT_CONNECTION_ACCESS_REPLY = 8; 89 static final int CALL_STATE_CHANGED = 9; 90 static final int DEVICE_STATE_CHANGED = 10; 91 static final int SEND_CLCC_RESPONSE = 11; 92 static final int SEND_VENDOR_SPECIFIC_RESULT_CODE = 12; 93 static final int SEND_BSIR = 13; 94 static final int DIALING_OUT_RESULT = 14; 95 static final int VOICE_RECOGNITION_RESULT = 15; 96 97 static final int STACK_EVENT = 101; 98 private static final int CLCC_RSP_TIMEOUT = 104; 99 100 private static final int CONNECT_TIMEOUT = 201; 101 102 private static final int CLCC_RSP_TIMEOUT_MS = 5000; 103 // NOTE: the value is not "final" - it is modified in the unit tests 104 @VisibleForTesting static int sConnectTimeoutMs = 30000; 105 106 // Number of times we should retry disconnecting audio before 107 // disconnecting the device. 108 private static final int MAX_RETRY_DISCONNECT_AUDIO = 3; 109 110 private static final HeadsetAgIndicatorEnableState DEFAULT_AG_INDICATOR_ENABLE_STATE = 111 new HeadsetAgIndicatorEnableState(true, true, true, true); 112 113 // State machine states 114 private final Disconnected mDisconnected = new Disconnected(); 115 private final Connecting mConnecting = new Connecting(); 116 private final Disconnecting mDisconnecting = new Disconnecting(); 117 private final Connected mConnected = new Connected(); 118 private final AudioOn mAudioOn = new AudioOn(); 119 private final AudioConnecting mAudioConnecting = new AudioConnecting(); 120 private final AudioDisconnecting mAudioDisconnecting = new AudioDisconnecting(); 121 private HeadsetStateBase mPrevState; 122 private HeadsetStateBase mCurrentState; 123 124 // Run time dependencies 125 private final BluetoothDevice mDevice; 126 private final HeadsetService mHeadsetService; 127 private final AdapterService mAdapterService; 128 private final HeadsetNativeInterface mNativeInterface; 129 private final HeadsetSystemInterface mSystemInterface; 130 private final DatabaseManager mDatabaseManager; 131 132 // Runtime states 133 @VisibleForTesting int mSpeakerVolume; 134 @VisibleForTesting int mMicVolume; 135 private boolean mDeviceSilenced; 136 private HeadsetAgIndicatorEnableState mAgIndicatorEnableState; 137 // The timestamp when the device entered connecting/connected state 138 private long mConnectingTimestampMs = Long.MIN_VALUE; 139 // Audio Parameters 140 private boolean mHasNrecEnabled = false; 141 private boolean mHasWbsEnabled = false; 142 private boolean mHasSwbLc3Enabled = false; 143 private boolean mHasSwbAptXEnabled = false; 144 // AT Phone book keeps a group of states used by AT+CPBR commands 145 @VisibleForTesting final AtPhonebook mPhonebook; 146 // HSP specific 147 private boolean mNeedDialingOutReply; 148 // Audio disconnect timeout retry count 149 private int mAudioDisconnectRetry = 0; 150 151 private BluetoothSinkAudioPolicy mHsClientAudioPolicy; 152 153 // Keys are AT commands, and values are the company IDs. 154 private static final Map<String, Integer> VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID; 155 156 static { 157 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap<>(); VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT, BluetoothAssignedNumbers.PLANTRONICS)158 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 159 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT, 160 BluetoothAssignedNumbers.PLANTRONICS); VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID, BluetoothAssignedNumbers.GOOGLE)161 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 162 BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID, 163 BluetoothAssignedNumbers.GOOGLE); VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XAPL, BluetoothAssignedNumbers.APPLE)164 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 165 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XAPL, 166 BluetoothAssignedNumbers.APPLE); VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV, BluetoothAssignedNumbers.APPLE)167 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 168 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV, 169 BluetoothAssignedNumbers.APPLE); VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGMI, BluetoothAssignedNumbers.GOOGLE)170 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 171 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGMI, 172 BluetoothAssignedNumbers.GOOGLE); VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGMR, BluetoothAssignedNumbers.GOOGLE)173 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 174 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGMR, 175 BluetoothAssignedNumbers.GOOGLE); VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGMM, BluetoothAssignedNumbers.GOOGLE)176 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 177 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGMM, 178 BluetoothAssignedNumbers.GOOGLE); VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGSN, BluetoothAssignedNumbers.GOOGLE)179 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put( 180 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGSN, 181 BluetoothAssignedNumbers.GOOGLE); 182 } 183 HeadsetStateMachine( BluetoothDevice device, Looper looper, HeadsetService headsetService, AdapterService adapterService, HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface)184 private HeadsetStateMachine( 185 BluetoothDevice device, 186 Looper looper, 187 HeadsetService headsetService, 188 AdapterService adapterService, 189 HeadsetNativeInterface nativeInterface, 190 HeadsetSystemInterface systemInterface) { 191 super(TAG, requireNonNull(looper)); 192 193 // Let the logging framework enforce the log level. TAG is set above in the parent 194 // constructor. 195 setDbg(true); 196 197 mDevice = requireNonNull(device); 198 mHeadsetService = requireNonNull(headsetService); 199 mNativeInterface = requireNonNull(nativeInterface); 200 mSystemInterface = requireNonNull(systemInterface); 201 mAdapterService = requireNonNull(adapterService); 202 mDatabaseManager = requireNonNull(adapterService.getDatabase()); 203 204 mDeviceSilenced = false; 205 206 BluetoothSinkAudioPolicy storedAudioPolicy = 207 mDatabaseManager.getAudioPolicyMetadata(device); 208 if (storedAudioPolicy == null) { 209 Log.w(TAG, "Audio Policy not created in database! Creating..."); 210 mHsClientAudioPolicy = new BluetoothSinkAudioPolicy.Builder().build(); 211 mDatabaseManager.setAudioPolicyMetadata(device, mHsClientAudioPolicy); 212 } else { 213 Log.i(TAG, "Audio Policy found in database!"); 214 mHsClientAudioPolicy = storedAudioPolicy; 215 } 216 217 // Create phonebook helper 218 mPhonebook = new AtPhonebook(mHeadsetService, mNativeInterface); 219 // Initialize state machine 220 addState(mDisconnected); 221 addState(mConnecting); 222 addState(mDisconnecting); 223 addState(mConnected); 224 addState(mAudioOn); 225 addState(mAudioConnecting); 226 addState(mAudioDisconnecting); 227 setInitialState(mDisconnected); 228 } 229 make( BluetoothDevice device, Looper looper, HeadsetService headsetService, AdapterService adapterService, HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface)230 static HeadsetStateMachine make( 231 BluetoothDevice device, 232 Looper looper, 233 HeadsetService headsetService, 234 AdapterService adapterService, 235 HeadsetNativeInterface nativeInterface, 236 HeadsetSystemInterface systemInterface) { 237 HeadsetStateMachine stateMachine = 238 new HeadsetStateMachine( 239 device, 240 looper, 241 headsetService, 242 adapterService, 243 nativeInterface, 244 systemInterface); 245 stateMachine.start(); 246 Log.i(TAG, "Created state machine " + stateMachine + " for " + device); 247 return stateMachine; 248 } 249 destroy(HeadsetStateMachine stateMachine)250 static void destroy(HeadsetStateMachine stateMachine) { 251 Log.i(TAG, "destroy"); 252 if (stateMachine == null) { 253 Log.w(TAG, "destroy(), stateMachine is null"); 254 return; 255 } 256 stateMachine.quitNow(); 257 stateMachine.cleanup(); 258 } 259 cleanup()260 public void cleanup() { 261 if (mPhonebook != null) { 262 mPhonebook.cleanup(); 263 } 264 mHasWbsEnabled = false; 265 mHasNrecEnabled = false; 266 mHasSwbLc3Enabled = false; 267 mHasSwbAptXEnabled = false; 268 } 269 dump(StringBuilder sb)270 public void dump(StringBuilder sb) { 271 ProfileService.println(sb, " mCurrentDevice: " + mDevice); 272 ProfileService.println(sb, " mCurrentState: " + mCurrentState); 273 ProfileService.println(sb, " mPrevState: " + mPrevState); 274 ProfileService.println(sb, " mConnectionState: " + getConnectionState()); 275 ProfileService.println(sb, " mAudioState: " + getAudioState()); 276 ProfileService.println(sb, " mNeedDialingOutReply: " + mNeedDialingOutReply); 277 ProfileService.println(sb, " mSpeakerVolume: " + mSpeakerVolume); 278 ProfileService.println(sb, " mMicVolume: " + mMicVolume); 279 ProfileService.println( 280 sb, " mConnectingTimestampMs(uptimeMillis): " + mConnectingTimestampMs); 281 ProfileService.println(sb, " mHsClientAudioPolicy: " + mHsClientAudioPolicy.toString()); 282 283 ProfileService.println(sb, " StateMachine: " + this); 284 // Dump the state machine logs 285 StringWriter stringWriter = new StringWriter(); 286 PrintWriter printWriter = new PrintWriter(stringWriter); 287 super.dump(new FileDescriptor(), printWriter, new String[] {}); 288 printWriter.flush(); 289 stringWriter.flush(); 290 ProfileService.println(sb, " StateMachineLog:"); 291 Scanner scanner = new Scanner(stringWriter.toString()); 292 while (scanner.hasNextLine()) { 293 String line = scanner.nextLine(); 294 ProfileService.println(sb, " " + line); 295 } 296 scanner.close(); 297 } 298 299 /** Base class for states used in this state machine to share common infrastructures */ 300 private abstract class HeadsetStateBase extends State { 301 @Override enter()302 public void enter() { 303 mCurrentState = this; 304 // Crash if mPrevState is null and state is not Disconnected 305 if (!(this instanceof Disconnected) && mPrevState == null) { 306 throw new IllegalStateException("mPrevState is null on enter()"); 307 } 308 enforceValidConnectionStateTransition(); 309 } 310 311 @Override exit()312 public void exit() { 313 mPrevState = this; 314 } 315 316 @Override toString()317 public String toString() { 318 return getName(); 319 } 320 321 /** 322 * Broadcast audio and connection state changes to the system. This should be called at the 323 * end of enter() method after all the setup is done 324 */ broadcastStateTransitions()325 void broadcastStateTransitions() { 326 if (mPrevState == null) { 327 return; 328 } 329 // TODO: Add STATE_AUDIO_DISCONNECTING constant to get rid of the 2nd part of this logic 330 if (getAudioStateInt() != mPrevState.getAudioStateInt() 331 || (mPrevState instanceof AudioDisconnecting && this instanceof AudioOn)) { 332 stateLogD("audio state changed: " + mDevice + ": " + mPrevState + " -> " + this); 333 broadcastAudioState(mDevice, mPrevState.getAudioStateInt(), getAudioStateInt()); 334 } 335 if (getConnectionStateInt() != mPrevState.getConnectionStateInt()) { 336 stateLogD( 337 "connection state changed: " + mDevice + ": " + mPrevState + " -> " + this); 338 broadcastConnectionState( 339 mDevice, mPrevState.getConnectionStateInt(), getConnectionStateInt()); 340 } 341 } 342 343 // Should not be called from enter() method broadcastConnectionState(BluetoothDevice device, int fromState, int toState)344 void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) { 345 stateLogD("broadcastConnectionState " + device + ": " + fromState + "->" + toState); 346 mHeadsetService.onConnectionStateChangedFromStateMachine(device, fromState, toState); 347 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 348 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); 349 intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); 350 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 351 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 352 mHeadsetService.sendBroadcastAsUser( 353 intent, 354 UserHandle.ALL, 355 BLUETOOTH_CONNECT, 356 Utils.getTempBroadcastOptions().toBundle()); 357 } 358 359 // Should not be called from enter() method broadcastAudioState(BluetoothDevice device, int fromState, int toState)360 void broadcastAudioState(BluetoothDevice device, int fromState, int toState) { 361 stateLogD("broadcastAudioState: " + device + ": " + fromState + "->" + toState); 362 // TODO(b/278520111): add metrics for SWB 363 BluetoothStatsLog.write( 364 BluetoothStatsLog.BLUETOOTH_SCO_CONNECTION_STATE_CHANGED, 365 mAdapterService.obfuscateAddress(device), 366 getConnectionStateFromAudioState(toState), 367 mHasWbsEnabled 368 ? BluetoothHfpProtoEnums.SCO_CODEC_MSBC 369 : BluetoothHfpProtoEnums.SCO_CODEC_CVSD, 370 mAdapterService.getMetricId(device)); 371 mHeadsetService.onAudioStateChangedFromStateMachine(device, fromState, toState); 372 Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 373 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); 374 intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); 375 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 376 mHeadsetService.sendBroadcastAsUser( 377 intent, 378 UserHandle.ALL, 379 BLUETOOTH_CONNECT, 380 Utils.getTempBroadcastOptions().toBundle()); 381 } 382 383 /** 384 * Verify if the current state transition is legal. This is supposed to be called from 385 * enter() method and crash if the state transition is out of the specification 386 * 387 * <p>Note: This method uses state objects to verify transition because these objects should 388 * be final and any other instances are invalid 389 */ enforceValidConnectionStateTransition()390 void enforceValidConnectionStateTransition() { 391 boolean result = false; 392 if (this == mDisconnected) { 393 result = 394 mPrevState == null 395 || mPrevState == mConnecting 396 || mPrevState == mDisconnecting 397 // TODO: edges to be removed after native stack refactoring 398 // all transitions to disconnected state should go through a pending 399 // state 400 // also, states should not go directly from an active audio state to 401 // disconnected state 402 || mPrevState == mConnected 403 || mPrevState == mAudioOn 404 || mPrevState == mAudioConnecting 405 || mPrevState == mAudioDisconnecting; 406 } else if (this == mConnecting) { 407 result = mPrevState == mDisconnected; 408 } else if (this == mDisconnecting) { 409 result = 410 mPrevState == mConnected 411 // TODO: edges to be removed after native stack refactoring 412 // all transitions to disconnecting state should go through 413 // connected state 414 || mPrevState == mAudioConnecting 415 || mPrevState == mAudioOn 416 || mPrevState == mAudioDisconnecting; 417 } else if (this == mConnected) { 418 result = 419 mPrevState == mConnecting 420 || mPrevState == mAudioDisconnecting 421 || mPrevState == mDisconnecting 422 || mPrevState == mAudioConnecting 423 // TODO: edges to be removed after native stack refactoring 424 // all transitions to connected state should go through a pending 425 // state 426 || mPrevState == mAudioOn 427 || mPrevState == mDisconnected; 428 } else if (this == mAudioConnecting) { 429 result = mPrevState == mConnected; 430 } else if (this == mAudioDisconnecting) { 431 result = mPrevState == mAudioOn; 432 } else if (this == mAudioOn) { 433 result = 434 mPrevState == mAudioConnecting 435 || mPrevState == mAudioDisconnecting 436 // TODO: edges to be removed after native stack refactoring 437 // all transitions to audio connected state should go through a 438 // pending 439 // state 440 || mPrevState == mConnected; 441 } 442 if (!result) { 443 throw new IllegalStateException( 444 "Invalid state transition from " 445 + mPrevState 446 + " to " 447 + this 448 + " for device " 449 + mDevice); 450 } 451 } 452 stateLogD(String msg)453 void stateLogD(String msg) { 454 log(getName() + ": currentDevice=" + mDevice + ", msg=" + msg); 455 } 456 stateLogW(String msg)457 void stateLogW(String msg) { 458 logw(getName() + ": currentDevice=" + mDevice + ", msg=" + msg); 459 } 460 stateLogE(String msg)461 void stateLogE(String msg) { 462 loge(getName() + ": currentDevice=" + mDevice + ", msg=" + msg); 463 } 464 stateLogV(String msg)465 void stateLogV(String msg) { 466 logv(getName() + ": currentDevice=" + mDevice + ", msg=" + msg); 467 } 468 stateLogI(String msg)469 void stateLogI(String msg) { 470 logi(getName() + ": currentDevice=" + mDevice + ", msg=" + msg); 471 } 472 stateLogWtf(String msg)473 void stateLogWtf(String msg) { 474 Log.wtf(TAG, getName() + ": " + msg); 475 } 476 477 /** 478 * Process connection event 479 * 480 * @param message the current message for the event 481 * @param state connection state to transition to 482 */ processConnectionEvent(Message message, int state)483 public void processConnectionEvent(Message message, int state) { 484 stateLogD( 485 "processConnectionEvent, state=" 486 + HeadsetHalConstants.getConnectionStateName(state) 487 + "[" 488 + state 489 + "]"); 490 } 491 492 /** 493 * Get a state value from {@link BluetoothProfile} that represents the connection state of 494 * this headset state 495 * 496 * @return a value in {@link BluetoothProfile#STATE_DISCONNECTED}, {@link 497 * BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or 498 * {@link BluetoothProfile#STATE_DISCONNECTING} 499 */ getConnectionStateInt()500 abstract int getConnectionStateInt(); 501 502 /** 503 * Get an audio state value from {@link BluetoothHeadset} 504 * 505 * @return a value in {@link BluetoothHeadset#STATE_AUDIO_DISCONNECTED}, {@link 506 * BluetoothHeadset#STATE_AUDIO_CONNECTING}, or {@link 507 * BluetoothHeadset#STATE_AUDIO_CONNECTED} 508 */ getAudioStateInt()509 abstract int getAudioStateInt(); 510 setAptxVoice(HeadsetCallState callState)511 protected void setAptxVoice(HeadsetCallState callState) { 512 if (!Flags.hfpCodecAptxVoice()) { 513 return; 514 } 515 if (!mHeadsetService.isAptXSwbEnabled()) { 516 return; 517 } 518 if (!mHeadsetService.isAptXSwbPmEnabled()) { 519 return; 520 } 521 if (mHeadsetService.isVirtualCallStarted()) { 522 stateLogD("CALL_STATE_CHANGED: enable AptX SWB for all voip calls "); 523 mHeadsetService.enableSwbCodec( 524 HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, true, mDevice); 525 } else if ((callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) 526 || (callState.mCallState == HeadsetHalConstants.CALL_STATE_INCOMING) 527 || ((callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE) 528 && (callState.mNumActive > 0))) { 529 if (!mSystemInterface.isHighDefCallInProgress()) { 530 stateLogD("CALL_STATE_CHANGED: disable AptX SWB for non-HD call "); 531 mHeadsetService.enableSwbCodec( 532 HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, false, mDevice); 533 mHasSwbAptXEnabled = false; 534 } else { 535 stateLogD("CALL_STATE_CHANGED: enable AptX SWB for HD call "); 536 mHeadsetService.enableSwbCodec( 537 HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, true, mDevice); 538 mHasSwbAptXEnabled = true; 539 } 540 } else { 541 stateLogD("CALL_STATE_CHANGED: AptX SWB state unchanged"); 542 } 543 } 544 } 545 546 class Disconnected extends HeadsetStateBase { 547 @Override getConnectionStateInt()548 int getConnectionStateInt() { 549 return BluetoothProfile.STATE_DISCONNECTED; 550 } 551 552 @Override getAudioStateInt()553 int getAudioStateInt() { 554 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 555 } 556 557 @Override enter()558 public void enter() { 559 super.enter(); 560 mConnectingTimestampMs = Long.MIN_VALUE; 561 mPhonebook.resetAtState(); 562 updateAgIndicatorEnableState(null); 563 mNeedDialingOutReply = false; 564 mHasWbsEnabled = false; 565 mHasSwbLc3Enabled = false; 566 mHasNrecEnabled = false; 567 mHasSwbAptXEnabled = false; 568 569 broadcastStateTransitions(); 570 logFailureIfNeeded(); 571 572 // Remove the state machine for unbonded devices 573 if (mPrevState != null 574 && mAdapterService.getBondState(mDevice) == BluetoothDevice.BOND_NONE) { 575 getHandler().post(() -> mHeadsetService.removeStateMachine(mDevice)); 576 } 577 } 578 579 @Override processMessage(Message message)580 public boolean processMessage(Message message) { 581 switch (message.what) { 582 case CONNECT: 583 BluetoothDevice device = (BluetoothDevice) message.obj; 584 stateLogD("Connecting to " + device); 585 if (!mDevice.equals(device)) { 586 stateLogE( 587 "CONNECT failed, device=" + device + ", currentDevice=" + mDevice); 588 break; 589 } 590 if (!mNativeInterface.connectHfp(device)) { 591 stateLogE("CONNECT failed for connectHfp(" + device + ")"); 592 // No state transition is involved, fire broadcast immediately 593 broadcastConnectionState( 594 device, 595 BluetoothProfile.STATE_DISCONNECTED, 596 BluetoothProfile.STATE_DISCONNECTED); 597 BluetoothStatsLog.write( 598 BluetoothStatsLog.BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED, 599 BluetoothProfile.HEADSET, 600 BluetoothProtoEnums.RESULT_FAILURE, 601 BluetoothProfile.STATE_DISCONNECTED, 602 BluetoothProfile.STATE_DISCONNECTED, 603 BluetoothProtoEnums.REASON_NATIVE_LAYER_REJECTED, 604 MetricsLogger.getInstance().getRemoteDeviceInfoProto(mDevice)); 605 break; 606 } 607 transitionTo(mConnecting); 608 break; 609 case DISCONNECT: 610 // ignore 611 break; 612 case CALL_STATE_CHANGED: 613 stateLogD("Ignoring CALL_STATE_CHANGED event"); 614 break; 615 case DEVICE_STATE_CHANGED: 616 stateLogD("Ignoring DEVICE_STATE_CHANGED event"); 617 break; 618 case STACK_EVENT: 619 HeadsetStackEvent event = (HeadsetStackEvent) message.obj; 620 stateLogD("STACK_EVENT: " + event); 621 if (!mDevice.equals(event.device)) { 622 stateLogE( 623 "Event device does not match currentDevice[" 624 + mDevice 625 + "], event: " 626 + event); 627 break; 628 } 629 switch (event.type) { 630 case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 631 processConnectionEvent(message, event.valueInt); 632 break; 633 default: 634 stateLogE("Unexpected stack event: " + event); 635 break; 636 } 637 break; 638 default: 639 stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message); 640 return NOT_HANDLED; 641 } 642 return HANDLED; 643 } 644 645 @Override processConnectionEvent(Message message, int state)646 public void processConnectionEvent(Message message, int state) { 647 super.processConnectionEvent(message, state); 648 switch (state) { 649 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 650 stateLogW("ignore DISCONNECTED event"); 651 break; 652 // Both events result in Connecting state as SLC establishment is still required 653 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 654 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 655 if (mHeadsetService.okToAcceptConnection(mDevice, false)) { 656 stateLogI("accept incoming connection"); 657 transitionTo(mConnecting); 658 } else { 659 stateLogI( 660 "rejected incoming HF, connectionPolicy=" 661 + mHeadsetService.getConnectionPolicy(mDevice) 662 + " bondState=" 663 + mAdapterService.getBondState(mDevice)); 664 // Reject the connection and stay in Disconnected state itself 665 if (!mNativeInterface.disconnectHfp(mDevice)) { 666 stateLogE("failed to disconnect"); 667 } 668 // Indicate rejection to other components. 669 broadcastConnectionState( 670 mDevice, 671 BluetoothProfile.STATE_DISCONNECTED, 672 BluetoothProfile.STATE_DISCONNECTED); 673 BluetoothStatsLog.write( 674 BluetoothStatsLog.BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED, 675 BluetoothProfile.HEADSET, 676 BluetoothProtoEnums.RESULT_FAILURE, 677 BluetoothProfile.STATE_DISCONNECTED, 678 BluetoothProfile.STATE_DISCONNECTED, 679 BluetoothProtoEnums.REASON_INCOMING_CONN_REJECTED, 680 MetricsLogger.getInstance().getRemoteDeviceInfoProto(mDevice)); 681 } 682 break; 683 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 684 stateLogW("Ignore DISCONNECTING event"); 685 break; 686 default: 687 stateLogE("Incorrect state: " + state); 688 break; 689 } 690 } 691 logFailureIfNeeded()692 private void logFailureIfNeeded() { 693 if (mPrevState == mConnecting || mPrevState == mDisconnected) { 694 // Result for disconnected -> disconnected is unknown as it should 695 // not have occurred. 696 int result = 697 (mPrevState == mConnecting) 698 ? BluetoothProtoEnums.RESULT_FAILURE 699 : BluetoothProtoEnums.RESULT_UNKNOWN; 700 701 BluetoothStatsLog.write( 702 BluetoothStatsLog.BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED, 703 BluetoothProfile.HEADSET, 704 result, 705 mPrevState.getConnectionStateInt(), 706 BluetoothProfile.STATE_DISCONNECTED, 707 BluetoothProtoEnums.REASON_UNEXPECTED_STATE, 708 MetricsLogger.getInstance().getRemoteDeviceInfoProto(mDevice)); 709 } 710 } 711 } 712 713 // Per HFP 1.7.1 spec page 23/144, Pending state needs to handle 714 // AT+BRSF, AT+CIND, AT+CMER, AT+BIND, AT+CHLD 715 // commands during SLC establishment 716 // AT+CHLD=? will be handled by statck directly 717 class Connecting extends HeadsetStateBase { 718 @Override getConnectionStateInt()719 int getConnectionStateInt() { 720 return BluetoothProfile.STATE_CONNECTING; 721 } 722 723 @Override getAudioStateInt()724 int getAudioStateInt() { 725 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 726 } 727 728 @Override enter()729 public void enter() { 730 super.enter(); 731 mConnectingTimestampMs = SystemClock.uptimeMillis(); 732 sendMessageDelayed(CONNECT_TIMEOUT, mDevice, sConnectTimeoutMs); 733 broadcastStateTransitions(); 734 } 735 736 @Override processMessage(Message message)737 public boolean processMessage(Message message) { 738 switch (message.what) { 739 case CONNECT: 740 case CONNECT_AUDIO: 741 case DISCONNECT: 742 deferMessage(message); 743 break; 744 case CONNECT_TIMEOUT: 745 { 746 // We timed out trying to connect, transition to Disconnected state 747 BluetoothDevice device = (BluetoothDevice) message.obj; 748 if (!mDevice.equals(device)) { 749 stateLogE("Unknown device timeout " + device); 750 break; 751 } 752 stateLogW("CONNECT_TIMEOUT"); 753 transitionTo(mDisconnected); 754 break; 755 } 756 case CALL_STATE_CHANGED: 757 HeadsetCallState callState = (HeadsetCallState) message.obj; 758 setAptxVoice(callState); 759 break; 760 case DEVICE_STATE_CHANGED: 761 stateLogD("ignoring DEVICE_STATE_CHANGED event"); 762 break; 763 case STACK_EVENT: 764 HeadsetStackEvent event = (HeadsetStackEvent) message.obj; 765 stateLogD("STACK_EVENT: " + event); 766 if (!mDevice.equals(event.device)) { 767 stateLogE( 768 "Event device does not match currentDevice[" 769 + mDevice 770 + "], event: " 771 + event); 772 break; 773 } 774 switch (event.type) { 775 case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 776 processConnectionEvent(message, event.valueInt); 777 break; 778 case HeadsetStackEvent.EVENT_TYPE_AT_CIND: 779 processAtCind(event.device); 780 break; 781 case HeadsetStackEvent.EVENT_TYPE_WBS: 782 processWBSEvent(event.valueInt); 783 break; 784 case HeadsetStackEvent.EVENT_TYPE_SWB: 785 processSWBEvent(event.valueInt, event.valueInt2); 786 break; 787 case HeadsetStackEvent.EVENT_TYPE_BIND: 788 processAtBind(event.valueString, event.device); 789 break; 790 // Unexpected AT commands, we only handle them for comparability reasons 791 case HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED: 792 stateLogW( 793 "Unexpected VR event, device=" 794 + event.device 795 + ", state=" 796 + event.valueInt); 797 processVrEvent(event.valueInt); 798 break; 799 case HeadsetStackEvent.EVENT_TYPE_DIAL_CALL: 800 stateLogW("Unexpected dial event, device=" + event.device); 801 processDialCall(event.valueString); 802 break; 803 case HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 804 stateLogW( 805 "Unexpected subscriber number event for" 806 + event.device 807 + ", state=" 808 + event.valueInt); 809 processSubscriberNumberRequest(event.device); 810 break; 811 case HeadsetStackEvent.EVENT_TYPE_AT_COPS: 812 stateLogW("Unexpected COPS event for " + event.device); 813 processAtCops(event.device); 814 break; 815 case HeadsetStackEvent.EVENT_TYPE_AT_CLCC: 816 Log.w(TAG, "Connecting: Unexpected CLCC event for" + event.device); 817 processAtClcc(event.device); 818 break; 819 case HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT: 820 stateLogW( 821 "Unexpected unknown AT event for" 822 + event.device 823 + ", cmd=" 824 + event.valueString); 825 processUnknownAt(event.valueString, event.device); 826 break; 827 case HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED: 828 stateLogW("Unexpected key-press event for " + event.device); 829 processKeyPressed(event.device); 830 break; 831 case HeadsetStackEvent.EVENT_TYPE_BIEV: 832 stateLogW( 833 "Unexpected BIEV event for " 834 + event.device 835 + ", indId=" 836 + event.valueInt 837 + ", indVal=" 838 + event.valueInt2); 839 processAtBiev(event.valueInt, event.valueInt2, event.device); 840 break; 841 case HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED: 842 stateLogW("Unexpected volume event for " + event.device); 843 processVolumeEvent(event.valueInt, event.valueInt2); 844 break; 845 case HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL: 846 stateLogW("Unexpected answer event for " + event.device); 847 mSystemInterface.answerCall(event.device); 848 break; 849 case HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL: 850 stateLogW("Unexpected hangup event for " + event.device); 851 mSystemInterface.hangupCall(event.device); 852 break; 853 default: 854 stateLogE("Unexpected event: " + event); 855 break; 856 } 857 break; 858 default: 859 stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message); 860 return NOT_HANDLED; 861 } 862 return HANDLED; 863 } 864 865 @Override processConnectionEvent(Message message, int state)866 public void processConnectionEvent(Message message, int state) { 867 super.processConnectionEvent(message, state); 868 switch (state) { 869 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 870 stateLogW("Disconnected"); 871 transitionTo(mDisconnected); 872 break; 873 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 874 stateLogD("RFCOMM connected"); 875 break; 876 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 877 stateLogD("SLC connected"); 878 transitionTo(mConnected); 879 break; 880 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 881 // Ignored 882 break; 883 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 884 stateLogW("Disconnecting"); 885 break; 886 default: 887 stateLogE("Incorrect state " + state); 888 break; 889 } 890 } 891 892 @Override exit()893 public void exit() { 894 removeMessages(CONNECT_TIMEOUT); 895 super.exit(); 896 } 897 } 898 899 class Disconnecting extends HeadsetStateBase { 900 @Override getConnectionStateInt()901 int getConnectionStateInt() { 902 return BluetoothProfile.STATE_DISCONNECTING; 903 } 904 905 @Override getAudioStateInt()906 int getAudioStateInt() { 907 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 908 } 909 910 @Override enter()911 public void enter() { 912 super.enter(); 913 sendMessageDelayed(CONNECT_TIMEOUT, mDevice, sConnectTimeoutMs); 914 broadcastStateTransitions(); 915 } 916 917 @Override processMessage(Message message)918 public boolean processMessage(Message message) { 919 switch (message.what) { 920 case CONNECT: 921 case CONNECT_AUDIO: 922 case DISCONNECT: 923 deferMessage(message); 924 break; 925 case CONNECT_TIMEOUT: 926 { 927 BluetoothDevice device = (BluetoothDevice) message.obj; 928 if (!mDevice.equals(device)) { 929 stateLogE("Unknown device timeout " + device); 930 break; 931 } 932 stateLogE("timeout"); 933 transitionTo(mDisconnected); 934 break; 935 } 936 case STACK_EVENT: 937 HeadsetStackEvent event = (HeadsetStackEvent) message.obj; 938 stateLogD("STACK_EVENT: " + event); 939 if (!mDevice.equals(event.device)) { 940 stateLogE( 941 "Event device does not match currentDevice[" 942 + mDevice 943 + "], event: " 944 + event); 945 break; 946 } 947 switch (event.type) { 948 case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 949 processConnectionEvent(message, event.valueInt); 950 break; 951 default: 952 stateLogE("Unexpected event: " + event); 953 break; 954 } 955 break; 956 default: 957 stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message); 958 return NOT_HANDLED; 959 } 960 return HANDLED; 961 } 962 963 // in Disconnecting state 964 @Override processConnectionEvent(Message message, int state)965 public void processConnectionEvent(Message message, int state) { 966 super.processConnectionEvent(message, state); 967 switch (state) { 968 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 969 stateLogD("processConnectionEvent: Disconnected"); 970 transitionTo(mDisconnected); 971 break; 972 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 973 stateLogD("processConnectionEvent: Connected"); 974 transitionTo(mConnected); 975 break; 976 default: 977 stateLogE("processConnectionEvent: Bad state: " + state); 978 break; 979 } 980 } 981 982 @Override exit()983 public void exit() { 984 removeMessages(CONNECT_TIMEOUT); 985 super.exit(); 986 } 987 } 988 989 /** Base class for Connected, AudioConnecting, AudioOn, AudioDisconnecting states */ 990 private abstract class ConnectedBase extends HeadsetStateBase { 991 @Override getConnectionStateInt()992 int getConnectionStateInt() { 993 return BluetoothProfile.STATE_CONNECTED; 994 } 995 996 /** 997 * Handle common messages in connected states. However, state specific messages must be 998 * handled individually. 999 * 1000 * @param message Incoming message to handle 1001 * @return True if handled successfully, False otherwise 1002 */ 1003 @Override processMessage(Message message)1004 public boolean processMessage(Message message) { 1005 switch (message.what) { 1006 case CONNECT: 1007 case DISCONNECT: 1008 case CONNECT_AUDIO: 1009 case DISCONNECT_AUDIO: 1010 case CONNECT_TIMEOUT: 1011 throw new IllegalStateException( 1012 "Illegal message in generic handler: " + message); 1013 case VOICE_RECOGNITION_START: 1014 { 1015 BluetoothDevice device = (BluetoothDevice) message.obj; 1016 if (!mDevice.equals(device)) { 1017 stateLogW( 1018 "VOICE_RECOGNITION_START failed " 1019 + device 1020 + " is not currentDevice"); 1021 break; 1022 } 1023 if (!mNativeInterface.startVoiceRecognition(mDevice)) { 1024 stateLogW("Failed to start voice recognition"); 1025 break; 1026 } 1027 break; 1028 } 1029 case VOICE_RECOGNITION_STOP: 1030 { 1031 BluetoothDevice device = (BluetoothDevice) message.obj; 1032 if (!mDevice.equals(device)) { 1033 stateLogW( 1034 "VOICE_RECOGNITION_STOP failed " 1035 + device 1036 + " is not currentDevice"); 1037 break; 1038 } 1039 if (!mNativeInterface.stopVoiceRecognition(mDevice)) { 1040 stateLogW("Failed to stop voice recognition"); 1041 break; 1042 } 1043 break; 1044 } 1045 case CALL_STATE_CHANGED: 1046 HeadsetCallState callState = (HeadsetCallState) message.obj; 1047 setAptxVoice(callState); 1048 1049 if (!mNativeInterface.phoneStateChange(mDevice, callState)) { 1050 stateLogW("processCallState: failed to update call state " + callState); 1051 break; 1052 } 1053 break; 1054 case DEVICE_STATE_CHANGED: 1055 if (mDeviceSilenced) { 1056 stateLogW( 1057 "DEVICE_STATE_CHANGED: " 1058 + mDevice 1059 + " is silenced, skip notify state changed."); 1060 break; 1061 } 1062 mNativeInterface.notifyDeviceStatus(mDevice, (HeadsetDeviceState) message.obj); 1063 break; 1064 case SEND_CLCC_RESPONSE: 1065 processSendClccResponse((HeadsetClccResponse) message.obj); 1066 break; 1067 case CLCC_RSP_TIMEOUT: 1068 { 1069 BluetoothDevice device = (BluetoothDevice) message.obj; 1070 if (!mDevice.equals(device)) { 1071 stateLogW( 1072 "CLCC_RSP_TIMEOUT failed " + device + " is not currentDevice"); 1073 break; 1074 } 1075 mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0); 1076 } 1077 break; 1078 case SEND_VENDOR_SPECIFIC_RESULT_CODE: 1079 processSendVendorSpecificResultCode( 1080 (HeadsetVendorSpecificResultCode) message.obj); 1081 break; 1082 case SEND_BSIR: 1083 mNativeInterface.sendBsir(mDevice, message.arg1 == 1); 1084 break; 1085 case VOICE_RECOGNITION_RESULT: 1086 { 1087 BluetoothDevice device = (BluetoothDevice) message.obj; 1088 if (!mDevice.equals(device)) { 1089 stateLogW( 1090 "VOICE_RECOGNITION_RESULT failed " 1091 + device 1092 + " is not currentDevice"); 1093 break; 1094 } 1095 mNativeInterface.atResponseCode( 1096 mDevice, 1097 message.arg1 == 1 1098 ? HeadsetHalConstants.AT_RESPONSE_OK 1099 : HeadsetHalConstants.AT_RESPONSE_ERROR, 1100 0); 1101 break; 1102 } 1103 case DIALING_OUT_RESULT: 1104 { 1105 BluetoothDevice device = (BluetoothDevice) message.obj; 1106 if (!mDevice.equals(device)) { 1107 stateLogW( 1108 "DIALING_OUT_RESULT failed " 1109 + device 1110 + " is not currentDevice"); 1111 break; 1112 } 1113 if (mNeedDialingOutReply) { 1114 mNeedDialingOutReply = false; 1115 mNativeInterface.atResponseCode( 1116 mDevice, 1117 message.arg1 == 1 1118 ? HeadsetHalConstants.AT_RESPONSE_OK 1119 : HeadsetHalConstants.AT_RESPONSE_ERROR, 1120 0); 1121 } 1122 } 1123 break; 1124 case INTENT_CONNECTION_ACCESS_REPLY: 1125 handleAccessPermissionResult((Intent) message.obj); 1126 break; 1127 case STACK_EVENT: 1128 HeadsetStackEvent event = (HeadsetStackEvent) message.obj; 1129 stateLogD("STACK_EVENT: " + event); 1130 if (!mDevice.equals(event.device)) { 1131 stateLogE( 1132 "Event device does not match currentDevice[" 1133 + mDevice 1134 + "], event: " 1135 + event); 1136 break; 1137 } 1138 switch (event.type) { 1139 case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 1140 processConnectionEvent(message, event.valueInt); 1141 break; 1142 case HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED: 1143 processAudioEvent(event.valueInt); 1144 break; 1145 case HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED: 1146 processVrEvent(event.valueInt); 1147 break; 1148 case HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL: 1149 mSystemInterface.answerCall(event.device); 1150 break; 1151 case HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL: 1152 mSystemInterface.hangupCall(event.device); 1153 break; 1154 case HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED: 1155 processVolumeEvent(event.valueInt, event.valueInt2); 1156 break; 1157 case HeadsetStackEvent.EVENT_TYPE_DIAL_CALL: 1158 processDialCall(event.valueString); 1159 break; 1160 case HeadsetStackEvent.EVENT_TYPE_SEND_DTMF: 1161 mSystemInterface.sendDtmf(event.valueInt, event.device); 1162 break; 1163 case HeadsetStackEvent.EVENT_TYPE_NOISE_REDUCTION: 1164 processNoiseReductionEvent(event.valueInt == 1); 1165 break; 1166 case HeadsetStackEvent.EVENT_TYPE_WBS: 1167 processWBSEvent(event.valueInt); 1168 break; 1169 case HeadsetStackEvent.EVENT_TYPE_SWB: 1170 processSWBEvent(event.valueInt, event.valueInt2); 1171 break; 1172 case HeadsetStackEvent.EVENT_TYPE_AT_CHLD: 1173 processAtChld(event.valueInt, event.device); 1174 break; 1175 case HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST: 1176 processSubscriberNumberRequest(event.device); 1177 break; 1178 case HeadsetStackEvent.EVENT_TYPE_AT_CIND: 1179 processAtCind(event.device); 1180 break; 1181 case HeadsetStackEvent.EVENT_TYPE_AT_COPS: 1182 processAtCops(event.device); 1183 break; 1184 case HeadsetStackEvent.EVENT_TYPE_AT_CLCC: 1185 processAtClcc(event.device); 1186 break; 1187 case HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT: 1188 processUnknownAt(event.valueString, event.device); 1189 break; 1190 case HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED: 1191 processKeyPressed(event.device); 1192 break; 1193 case HeadsetStackEvent.EVENT_TYPE_BIND: 1194 processAtBind(event.valueString, event.device); 1195 break; 1196 case HeadsetStackEvent.EVENT_TYPE_BIEV: 1197 processAtBiev(event.valueInt, event.valueInt2, event.device); 1198 break; 1199 case HeadsetStackEvent.EVENT_TYPE_BIA: 1200 updateAgIndicatorEnableState( 1201 (HeadsetAgIndicatorEnableState) event.valueObject); 1202 break; 1203 default: 1204 stateLogE("Unknown stack event: " + event); 1205 break; 1206 } 1207 break; 1208 default: 1209 stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message); 1210 return NOT_HANDLED; 1211 } 1212 return HANDLED; 1213 } 1214 1215 @Override processConnectionEvent(Message message, int state)1216 public void processConnectionEvent(Message message, int state) { 1217 super.processConnectionEvent(message, state); 1218 switch (state) { 1219 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 1220 stateLogE("processConnectionEvent: RFCOMM connected again, shouldn't happen"); 1221 break; 1222 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED: 1223 stateLogE("processConnectionEvent: SLC connected again, shouldn't happen"); 1224 break; 1225 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING: 1226 stateLogI("processConnectionEvent: Disconnecting"); 1227 transitionTo(mDisconnecting); 1228 break; 1229 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED: 1230 stateLogI("processConnectionEvent: Disconnected"); 1231 transitionTo(mDisconnected); 1232 break; 1233 default: 1234 stateLogE("processConnectionEvent: bad state: " + state); 1235 break; 1236 } 1237 } 1238 1239 /** 1240 * Each state should handle audio events differently 1241 * 1242 * @param state audio state 1243 */ processAudioEvent(int state)1244 public abstract void processAudioEvent(int state); 1245 } 1246 1247 class Connected extends ConnectedBase { 1248 @Override getAudioStateInt()1249 int getAudioStateInt() { 1250 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 1251 } 1252 1253 @Override enter()1254 public void enter() { 1255 super.enter(); 1256 if (mPrevState == mConnecting) { 1257 // Reset AG indicator subscriptions, HF can set this later using AT+BIA command 1258 updateAgIndicatorEnableState(DEFAULT_AG_INDICATOR_ENABLE_STATE); 1259 // Reset NREC on connect event. Headset will override later 1260 processNoiseReductionEvent(true); 1261 // Query phone state for initial setup 1262 mSystemInterface.queryPhoneState(); 1263 // Remove pending connection attempts that were deferred during the pending 1264 // state. This is to prevent auto connect attempts from disconnecting 1265 // devices that previously successfully connected. 1266 removeDeferredMessages(CONNECT); 1267 } else if (mPrevState == mAudioDisconnecting) { 1268 // Reset audio disconnecting retry count. Either the disconnection was successful 1269 // or the retry count reached MAX_RETRY_DISCONNECT_AUDIO. 1270 mAudioDisconnectRetry = 0; 1271 } 1272 1273 broadcastStateTransitions(); 1274 logSuccessIfNeeded(); 1275 } 1276 1277 @Override processMessage(Message message)1278 public boolean processMessage(Message message) { 1279 switch (message.what) { 1280 case CONNECT: 1281 { 1282 BluetoothDevice device = (BluetoothDevice) message.obj; 1283 stateLogW( 1284 "CONNECT, ignored, device=" + device + ", currentDevice" + mDevice); 1285 break; 1286 } 1287 case DISCONNECT: 1288 { 1289 BluetoothDevice device = (BluetoothDevice) message.obj; 1290 stateLogD("DISCONNECT from device=" + device); 1291 if (!mDevice.equals(device)) { 1292 stateLogW("DISCONNECT, device " + device + " not connected"); 1293 break; 1294 } 1295 if (!mNativeInterface.disconnectHfp(device)) { 1296 // broadcast immediately as no state transition is involved 1297 stateLogE("DISCONNECT from " + device + " failed"); 1298 broadcastConnectionState( 1299 device, 1300 BluetoothProfile.STATE_CONNECTED, 1301 BluetoothProfile.STATE_CONNECTED); 1302 break; 1303 } 1304 transitionTo(mDisconnecting); 1305 } 1306 break; 1307 case CONNECT_AUDIO: 1308 stateLogD("CONNECT_AUDIO, device=" + mDevice); 1309 if (Utils.isScoManagedByAudioEnabled()) { 1310 stateLogD("ScoManagedByAudioEnabled, BT does not CONNECT_AUDIO"); 1311 transitionTo(mAudioConnecting); 1312 break; 1313 } 1314 mSystemInterface.getAudioManager().setA2dpSuspended(true); 1315 if (isAtLeastU()) { 1316 mSystemInterface.getAudioManager().setLeAudioSuspended(true); 1317 } 1318 1319 if (Flags.hfpCodecAptxVoice() 1320 && mHeadsetService.isAptXSwbEnabled() 1321 && mHeadsetService.isAptXSwbPmEnabled()) { 1322 if (!mHeadsetService.isVirtualCallStarted() 1323 && mSystemInterface.isHighDefCallInProgress()) { 1324 stateLogD("CONNECT_AUDIO: enable AptX SWB for HD call "); 1325 mHeadsetService.enableSwbCodec( 1326 HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, true, mDevice); 1327 } else { 1328 stateLogD("CONNECT_AUDIO: disable AptX SWB for non-HD or Voip calls"); 1329 mHeadsetService.enableSwbCodec( 1330 HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, false, mDevice); 1331 } 1332 } 1333 1334 if (!mNativeInterface.connectAudio(mDevice)) { 1335 mSystemInterface.getAudioManager().setA2dpSuspended(false); 1336 if (isAtLeastU()) { 1337 mSystemInterface.getAudioManager().setLeAudioSuspended(false); 1338 } 1339 stateLogE("Failed to connect SCO audio for " + mDevice); 1340 // No state change involved, fire broadcast immediately 1341 broadcastAudioState( 1342 mDevice, 1343 BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 1344 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1345 break; 1346 } 1347 transitionTo(mAudioConnecting); 1348 break; 1349 case DISCONNECT_AUDIO: 1350 stateLogD("ignore DISCONNECT_AUDIO, device=" + mDevice); 1351 // ignore 1352 break; 1353 default: 1354 return super.processMessage(message); 1355 } 1356 return HANDLED; 1357 } 1358 1359 @Override processAudioEvent(int state)1360 public void processAudioEvent(int state) { 1361 stateLogD("processAudioEvent, state=" + state); 1362 switch (state) { 1363 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 1364 if (mHeadsetService.isScoAcceptable(mDevice) != BluetoothStatusCodes.SUCCESS) { 1365 stateLogW("processAudioEvent: reject incoming audio connection"); 1366 if (!mNativeInterface.disconnectAudio(mDevice)) { 1367 stateLogE("processAudioEvent: failed to disconnect audio"); 1368 } 1369 // Indicate rejection to other components. 1370 broadcastAudioState( 1371 mDevice, 1372 BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 1373 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1374 break; 1375 } 1376 stateLogI("processAudioEvent: audio connected"); 1377 transitionTo(mAudioOn); 1378 break; 1379 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 1380 if (mHeadsetService.isScoAcceptable(mDevice) != BluetoothStatusCodes.SUCCESS) { 1381 stateLogW("processAudioEvent: reject incoming pending audio connection"); 1382 if (!mNativeInterface.disconnectAudio(mDevice)) { 1383 stateLogE("processAudioEvent: failed to disconnect pending audio"); 1384 } 1385 // Indicate rejection to other components. 1386 broadcastAudioState( 1387 mDevice, 1388 BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 1389 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1390 break; 1391 } 1392 stateLogI("processAudioEvent: audio connecting"); 1393 transitionTo(mAudioConnecting); 1394 break; 1395 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1396 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1397 // ignore 1398 break; 1399 default: 1400 stateLogE("processAudioEvent: bad state: " + state); 1401 break; 1402 } 1403 } 1404 logSuccessIfNeeded()1405 private void logSuccessIfNeeded() { 1406 if (mPrevState == mConnecting || mPrevState == mDisconnected) { 1407 BluetoothStatsLog.write( 1408 BluetoothStatsLog.BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED, 1409 BluetoothProfile.HEADSET, 1410 BluetoothProtoEnums.RESULT_SUCCESS, 1411 mPrevState.getConnectionStateInt(), 1412 BluetoothProfile.STATE_CONNECTED, 1413 BluetoothProtoEnums.REASON_SUCCESS, 1414 MetricsLogger.getInstance().getRemoteDeviceInfoProto(mDevice)); 1415 } 1416 } 1417 } 1418 1419 class AudioConnecting extends ConnectedBase { 1420 @Override getAudioStateInt()1421 int getAudioStateInt() { 1422 return BluetoothHeadset.STATE_AUDIO_CONNECTING; 1423 } 1424 1425 @Override enter()1426 public void enter() { 1427 super.enter(); 1428 sendMessageDelayed(CONNECT_TIMEOUT, mDevice, sConnectTimeoutMs); 1429 broadcastStateTransitions(); 1430 } 1431 1432 @Override processMessage(Message message)1433 public boolean processMessage(Message message) { 1434 switch (message.what) { 1435 case CONNECT: 1436 case DISCONNECT: 1437 case CONNECT_AUDIO: 1438 case DISCONNECT_AUDIO: 1439 deferMessage(message); 1440 break; 1441 case CONNECT_TIMEOUT: 1442 { 1443 BluetoothDevice device = (BluetoothDevice) message.obj; 1444 if (!mDevice.equals(device)) { 1445 stateLogW("CONNECT_TIMEOUT for unknown device " + device); 1446 break; 1447 } 1448 stateLogW("CONNECT_TIMEOUT"); 1449 transitionTo(mConnected); 1450 break; 1451 } 1452 default: 1453 return super.processMessage(message); 1454 } 1455 return HANDLED; 1456 } 1457 1458 @Override processAudioEvent(int state)1459 public void processAudioEvent(int state) { 1460 switch (state) { 1461 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1462 stateLogW("processAudioEvent: audio connection failed"); 1463 transitionTo(mConnected); 1464 break; 1465 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 1466 // ignore, already in audio connecting state 1467 break; 1468 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1469 // ignore, there is no BluetoothHeadset.STATE_AUDIO_DISCONNECTING 1470 break; 1471 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 1472 stateLogI("processAudioEvent: audio connected"); 1473 transitionTo(mAudioOn); 1474 break; 1475 default: 1476 stateLogE("processAudioEvent: bad state: " + state); 1477 break; 1478 } 1479 } 1480 1481 @Override exit()1482 public void exit() { 1483 removeMessages(CONNECT_TIMEOUT); 1484 super.exit(); 1485 } 1486 } 1487 1488 class MyAudioServerStateCallback extends AudioManager.AudioServerStateCallback { 1489 @Override onAudioServerDown()1490 public void onAudioServerDown() { 1491 logi("onAudioServerDown"); 1492 } 1493 1494 @Override onAudioServerUp()1495 public void onAudioServerUp() { 1496 logi("onAudioServerUp restoring audio parameters"); 1497 setAudioParameters(); 1498 } 1499 } 1500 1501 MyAudioServerStateCallback mAudioServerStateCallback = new MyAudioServerStateCallback(); 1502 1503 class AudioOn extends ConnectedBase { 1504 @Override getAudioStateInt()1505 int getAudioStateInt() { 1506 return BluetoothHeadset.STATE_AUDIO_CONNECTED; 1507 } 1508 1509 @Override enter()1510 public void enter() { 1511 super.enter(); 1512 removeDeferredMessages(CONNECT_AUDIO); 1513 // Set active device to current active SCO device when the current active device 1514 // is different from mCurrentDevice. This is to accommodate active device state 1515 // mis-match between native and Java. 1516 if (!mDevice.equals(mHeadsetService.getActiveDevice()) 1517 && !hasDeferredMessages(DISCONNECT_AUDIO)) { 1518 mHeadsetService.setActiveDevice(mDevice); 1519 } 1520 1521 // TODO (b/276463350): Remove check when Express metrics no longer need jni 1522 if (!Utils.isInstrumentationTestMode()) { 1523 if (mHasSwbLc3Enabled) { 1524 Counter.logIncrement("bluetooth.value_lc3_codec_usage_over_hfp"); 1525 } else if (mHasSwbAptXEnabled) { 1526 Counter.logIncrement("bluetooth.value_aptx_codec_usage_over_hfp"); 1527 } else if (mHasWbsEnabled) { 1528 Counter.logIncrement("bluetooth.value_msbc_codec_usage_over_hfp"); 1529 } else { 1530 Counter.logIncrement("bluetooth.value_cvsd_codec_usage_over_hfp"); 1531 } 1532 } 1533 1534 setAudioParameters(); 1535 1536 mSystemInterface 1537 .getAudioManager() 1538 .setAudioServerStateCallback( 1539 mHeadsetService.getMainExecutor(), mAudioServerStateCallback); 1540 1541 broadcastStateTransitions(); 1542 } 1543 1544 @Override exit()1545 public void exit() { 1546 super.exit(); 1547 1548 mSystemInterface.getAudioManager().clearAudioServerStateCallback(); 1549 } 1550 1551 @Override processMessage(Message message)1552 public boolean processMessage(Message message) { 1553 switch (message.what) { 1554 case CONNECT: 1555 { 1556 BluetoothDevice device = (BluetoothDevice) message.obj; 1557 stateLogW( 1558 "CONNECT, ignored, device=" + device + ", currentDevice" + mDevice); 1559 break; 1560 } 1561 case DISCONNECT: 1562 { 1563 BluetoothDevice device = (BluetoothDevice) message.obj; 1564 stateLogD("DISCONNECT, device=" + device); 1565 if (!mDevice.equals(device)) { 1566 stateLogW("DISCONNECT, device " + device + " not connected"); 1567 break; 1568 } 1569 // Disconnect BT SCO first 1570 if (!mNativeInterface.disconnectAudio(mDevice)) { 1571 stateLogW("DISCONNECT failed, device=" + mDevice); 1572 // if disconnect BT SCO failed, transition to mConnected state to force 1573 // disconnect device 1574 } 1575 deferMessage(obtainMessage(DISCONNECT, mDevice)); 1576 transitionTo(mAudioDisconnecting); 1577 break; 1578 } 1579 case CONNECT_AUDIO: 1580 { 1581 BluetoothDevice device = (BluetoothDevice) message.obj; 1582 if (!mDevice.equals(device)) { 1583 stateLogW("CONNECT_AUDIO device is not connected " + device); 1584 break; 1585 } 1586 stateLogW("CONNECT_AUDIO device audio is already connected " + device); 1587 break; 1588 } 1589 case DISCONNECT_AUDIO: 1590 { 1591 BluetoothDevice device = (BluetoothDevice) message.obj; 1592 if (!mDevice.equals(device)) { 1593 stateLogW( 1594 "DISCONNECT_AUDIO, failed, device=" 1595 + device 1596 + ", currentDevice=" 1597 + mDevice); 1598 break; 1599 } 1600 if (mNativeInterface.disconnectAudio(mDevice)) { 1601 stateLogD("DISCONNECT_AUDIO, device=" + mDevice); 1602 transitionTo(mAudioDisconnecting); 1603 } else { 1604 stateLogW("DISCONNECT_AUDIO failed, device=" + mDevice); 1605 broadcastAudioState( 1606 mDevice, 1607 BluetoothHeadset.STATE_AUDIO_CONNECTED, 1608 BluetoothHeadset.STATE_AUDIO_CONNECTED); 1609 } 1610 break; 1611 } 1612 case INTENT_SCO_VOLUME_CHANGED: 1613 processIntentScoVolume((Intent) message.obj, mDevice); 1614 break; 1615 case STACK_EVENT: 1616 HeadsetStackEvent event = (HeadsetStackEvent) message.obj; 1617 stateLogD("STACK_EVENT: " + event); 1618 if (!mDevice.equals(event.device)) { 1619 stateLogE( 1620 "Event device does not match currentDevice[" 1621 + mDevice 1622 + "], event: " 1623 + event); 1624 break; 1625 } 1626 switch (event.type) { 1627 case HeadsetStackEvent.EVENT_TYPE_WBS: 1628 stateLogE("Cannot change WBS state when audio is connected: " + event); 1629 break; 1630 case HeadsetStackEvent.EVENT_TYPE_SWB: 1631 stateLogE("Cannot change SWB state when audio is connected: " + event); 1632 break; 1633 default: 1634 super.processMessage(message); 1635 break; 1636 } 1637 break; 1638 default: 1639 return super.processMessage(message); 1640 } 1641 return HANDLED; 1642 } 1643 1644 @Override processAudioEvent(int state)1645 public void processAudioEvent(int state) { 1646 switch (state) { 1647 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1648 stateLogI("processAudioEvent: audio disconnected by remote"); 1649 transitionTo(mConnected); 1650 break; 1651 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1652 stateLogI("processAudioEvent: audio being disconnected by remote"); 1653 transitionTo(mAudioDisconnecting); 1654 break; 1655 default: 1656 stateLogE("processAudioEvent: bad state: " + state); 1657 break; 1658 } 1659 } 1660 processIntentScoVolume(Intent intent, BluetoothDevice device)1661 private void processIntentScoVolume(Intent intent, BluetoothDevice device) { 1662 int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 1663 stateLogD( 1664 "processIntentScoVolume: mSpeakerVolume=" 1665 + mSpeakerVolume 1666 + ", volumeValue=" 1667 + volumeValue); 1668 if (mSpeakerVolume != volumeValue) { 1669 mSpeakerVolume = volumeValue; 1670 mNativeInterface.setVolume( 1671 device, HeadsetHalConstants.VOLUME_TYPE_SPK, mSpeakerVolume); 1672 } 1673 } 1674 } 1675 1676 class AudioDisconnecting extends ConnectedBase { 1677 @Override getAudioStateInt()1678 int getAudioStateInt() { 1679 // TODO: need BluetoothHeadset.STATE_AUDIO_DISCONNECTING 1680 return BluetoothHeadset.STATE_AUDIO_CONNECTED; 1681 } 1682 1683 @Override enter()1684 public void enter() { 1685 super.enter(); 1686 sendMessageDelayed(CONNECT_TIMEOUT, mDevice, sConnectTimeoutMs); 1687 broadcastStateTransitions(); 1688 } 1689 1690 @Override processMessage(Message message)1691 public boolean processMessage(Message message) { 1692 switch (message.what) { 1693 case CONNECT: 1694 case DISCONNECT: 1695 case CONNECT_AUDIO: 1696 case DISCONNECT_AUDIO: 1697 deferMessage(message); 1698 break; 1699 case CONNECT_TIMEOUT: 1700 { 1701 BluetoothDevice device = (BluetoothDevice) message.obj; 1702 if (!mDevice.equals(device)) { 1703 stateLogW("CONNECT_TIMEOUT for unknown device " + device); 1704 break; 1705 } 1706 if (mAudioDisconnectRetry == MAX_RETRY_DISCONNECT_AUDIO) { 1707 stateLogW("CONNECT_TIMEOUT: Disconnecting device"); 1708 // Restoring state to Connected with message DISCONNECT 1709 deferMessage(obtainMessage(DISCONNECT, mDevice)); 1710 transitionTo(mConnected); 1711 } else { 1712 mAudioDisconnectRetry += 1; 1713 stateLogW( 1714 "CONNECT_TIMEOUT: retrying " 1715 + (MAX_RETRY_DISCONNECT_AUDIO - mAudioDisconnectRetry) 1716 + " more time(s)"); 1717 transitionTo(mAudioOn); 1718 } 1719 break; 1720 } 1721 default: 1722 return super.processMessage(message); 1723 } 1724 return HANDLED; 1725 } 1726 1727 @Override processAudioEvent(int state)1728 public void processAudioEvent(int state) { 1729 switch (state) { 1730 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED: 1731 stateLogI("processAudioEvent: audio disconnected"); 1732 transitionTo(mConnected); 1733 break; 1734 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING: 1735 // ignore 1736 break; 1737 case HeadsetHalConstants.AUDIO_STATE_CONNECTED: 1738 stateLogW("processAudioEvent: audio disconnection failed"); 1739 // Audio connected, resetting disconnect retry. 1740 mAudioDisconnectRetry = 0; 1741 transitionTo(mAudioOn); 1742 break; 1743 case HeadsetHalConstants.AUDIO_STATE_CONNECTING: 1744 // ignore, see if it goes into connected state, otherwise, timeout 1745 break; 1746 default: 1747 stateLogE("processAudioEvent: bad state: " + state); 1748 break; 1749 } 1750 } 1751 1752 @Override exit()1753 public void exit() { 1754 removeMessages(CONNECT_TIMEOUT); 1755 super.exit(); 1756 } 1757 } 1758 1759 /** 1760 * Get the underlying device tracked by this state machine 1761 * 1762 * @return device in focus 1763 */ 1764 @VisibleForTesting getDevice()1765 public BluetoothDevice getDevice() { 1766 return mDevice; 1767 } 1768 1769 /** 1770 * Get the current connection state of this state machine 1771 * 1772 * @return current connection state, one of {@link BluetoothProfile#STATE_DISCONNECTED}, {@link 1773 * BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or {@link 1774 * BluetoothProfile#STATE_DISCONNECTING} 1775 */ 1776 @VisibleForTesting getConnectionState()1777 public synchronized int getConnectionState() { 1778 if (mCurrentState == null) { 1779 return BluetoothHeadset.STATE_DISCONNECTED; 1780 } 1781 return mCurrentState.getConnectionStateInt(); 1782 } 1783 1784 /** 1785 * Get the current audio state of this state machine 1786 * 1787 * @return current audio state, one of {@link BluetoothHeadset#STATE_AUDIO_DISCONNECTED}, {@link 1788 * BluetoothHeadset#STATE_AUDIO_CONNECTING}, or {@link 1789 * BluetoothHeadset#STATE_AUDIO_CONNECTED} 1790 */ getAudioState()1791 public synchronized int getAudioState() { 1792 if (mCurrentState == null) { 1793 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 1794 } 1795 return mCurrentState.getAudioStateInt(); 1796 } 1797 getConnectingTimestampMs()1798 public long getConnectingTimestampMs() { 1799 return mConnectingTimestampMs; 1800 } 1801 1802 /** 1803 * Set the silence mode status of this state machine 1804 * 1805 * @param silence true to enter silence mode, false on exit 1806 * @return true on success, false on error 1807 */ 1808 @VisibleForTesting setSilenceDevice(boolean silence)1809 public boolean setSilenceDevice(boolean silence) { 1810 if (silence == mDeviceSilenced) { 1811 return false; 1812 } 1813 if (silence) { 1814 mSystemInterface 1815 .getHeadsetPhoneState() 1816 .listenForPhoneState(mDevice, PhoneStateListener.LISTEN_NONE); 1817 } else { 1818 updateAgIndicatorEnableState(mAgIndicatorEnableState); 1819 } 1820 mDeviceSilenced = silence; 1821 return true; 1822 } 1823 1824 /* 1825 * Put the AT command, company ID, arguments, and device in an Intent and broadcast it. 1826 */ 1827 @VisibleForTesting broadcastVendorSpecificEventIntent( String command, int companyId, int commandType, Object[] arguments, BluetoothDevice device)1828 void broadcastVendorSpecificEventIntent( 1829 String command, 1830 int companyId, 1831 int commandType, 1832 Object[] arguments, 1833 BluetoothDevice device) { 1834 log("broadcastVendorSpecificEventIntent(" + command + ")"); 1835 mAdapterService 1836 .getRemoteDevices() 1837 .handleVendorSpecificHeadsetEvent( 1838 device, command, companyId, commandType, arguments); 1839 1840 Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT); 1841 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command); 1842 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType); 1843 // assert: all elements of args are Serializable 1844 intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments); 1845 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1846 intent.addCategory( 1847 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY 1848 + "." 1849 + Integer.toString(companyId)); 1850 mHeadsetService.sendBroadcastAsUser( 1851 intent, 1852 UserHandle.ALL, 1853 BLUETOOTH_CONNECT, 1854 Utils.getTempBroadcastOptions().toBundle()); 1855 } 1856 setAudioParameters()1857 private void setAudioParameters() { 1858 if (Utils.isScoManagedByAudioEnabled()) { 1859 Log.i(TAG, "isScoManagedByAudio enabled, do not setAudioParameters"); 1860 return; 1861 } 1862 AudioManager am = mSystemInterface.getAudioManager(); 1863 Log.i( 1864 TAG, 1865 ("setAudioParameters for " + mDevice + ":") 1866 + (" Name=" + getCurrentDeviceName()) 1867 + (" hasNrecEnabled=" + mHasNrecEnabled) 1868 + (" hasWbsEnabled=" + mHasWbsEnabled) 1869 + (" hasSwbEnabled=" + mHasSwbLc3Enabled) 1870 + (" hasAptXSwbEnabled=" + mHasSwbAptXEnabled)); 1871 am.setParameters("bt_lc3_swb=" + (mHasSwbLc3Enabled ? "on" : "off")); 1872 if (Flags.hfpCodecAptxVoice() && mHeadsetService.isAptXSwbEnabled()) { 1873 /* AptX bt_swb: 0 -> on, 65535 -> off */ 1874 am.setParameters("bt_swb=" + (mHasSwbAptXEnabled ? "0" : "65535")); 1875 } 1876 am.setBluetoothHeadsetProperties(getCurrentDeviceName(), mHasNrecEnabled, mHasWbsEnabled); 1877 } 1878 1879 @VisibleForTesting parseUnknownAt(String atString)1880 String parseUnknownAt(String atString) { 1881 StringBuilder atCommand = new StringBuilder(atString.length()); 1882 1883 for (int i = 0; i < atString.length(); i++) { 1884 char c = atString.charAt(i); 1885 if (c == '"') { 1886 int j = atString.indexOf('"', i + 1); // search for closing " 1887 if (j == -1) { // unmatched ", insert one. 1888 atCommand.append(atString.substring(i, atString.length())); 1889 atCommand.append('"'); 1890 break; 1891 } 1892 atCommand.append(atString.substring(i, j + 1)); 1893 i = j; 1894 } else if (c != ' ') { 1895 atCommand.append(Character.toUpperCase(c)); 1896 } 1897 } 1898 return atCommand.toString(); 1899 } 1900 1901 @VisibleForTesting getAtCommandType(String atCommand)1902 int getAtCommandType(String atCommand) { 1903 int commandType = AtPhonebook.TYPE_UNKNOWN; 1904 String atString = null; 1905 atCommand = atCommand.trim(); 1906 if (atCommand.length() > 5) { 1907 atString = atCommand.substring(5); 1908 if (atString.startsWith("?")) { // Read 1909 commandType = AtPhonebook.TYPE_READ; 1910 } else if (atString.startsWith("=?")) { // Test 1911 commandType = AtPhonebook.TYPE_TEST; 1912 } else if (atString.startsWith("=")) { // Set 1913 commandType = AtPhonebook.TYPE_SET; 1914 } else { 1915 commandType = AtPhonebook.TYPE_UNKNOWN; 1916 } 1917 } 1918 return commandType; 1919 } 1920 1921 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) processDialCall(String number)1922 private void processDialCall(String number) { 1923 String dialNumber; 1924 if (mHeadsetService.hasDeviceInitiatedDialingOut()) { 1925 Log.w(TAG, "processDialCall, already dialling"); 1926 mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1927 return; 1928 } 1929 if ((number == null) || (number.length() == 0)) { 1930 dialNumber = mPhonebook.getLastDialledNumber(); 1931 if (dialNumber == null) { 1932 Log.w(TAG, "processDialCall, last dial number null"); 1933 mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1934 return; 1935 } 1936 } else if (number.charAt(0) == '>') { 1937 // Yuck - memory dialling requested. 1938 // Just dial last number for now 1939 if (number.startsWith(">9999")) { // for PTS test 1940 Log.w(TAG, "Number is too big"); 1941 mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1942 return; 1943 } 1944 log("processDialCall, memory dial do last dial for now"); 1945 dialNumber = mPhonebook.getLastDialledNumber(); 1946 if (dialNumber == null) { 1947 Log.w(TAG, "processDialCall, last dial number null"); 1948 mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1949 return; 1950 } 1951 } else { 1952 // Remove trailing ';' 1953 if (number.charAt(number.length() - 1) == ';') { 1954 number = number.substring(0, number.length() - 1); 1955 } 1956 dialNumber = Utils.convertPreDial(number); 1957 } 1958 if (!mHeadsetService.dialOutgoingCall(mDevice, dialNumber)) { 1959 Log.w(TAG, "processDialCall, failed to dial in service"); 1960 mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1961 return; 1962 } 1963 mNeedDialingOutReply = true; 1964 } 1965 1966 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) processVrEvent(int state)1967 private void processVrEvent(int state) { 1968 if (state == HeadsetHalConstants.VR_STATE_STARTED) { 1969 if (!mHeadsetService.startVoiceRecognitionByHeadset(mDevice)) { 1970 mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1971 } 1972 } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) { 1973 if (mHeadsetService.stopVoiceRecognitionByHeadset(mDevice)) { 1974 mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_OK, 0); 1975 } else { 1976 mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1977 } 1978 } else { 1979 mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 1980 } 1981 } 1982 1983 @VisibleForTesting processVolumeEvent(int volumeType, int volume)1984 void processVolumeEvent(int volumeType, int volume) { 1985 // Only current active device can change SCO volume 1986 if (!mDevice.equals(mHeadsetService.getActiveDevice())) { 1987 Log.w(TAG, "processVolumeEvent, ignored because " + mDevice + " is not active"); 1988 return; 1989 } 1990 if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) { 1991 mSpeakerVolume = volume; 1992 int flag = (mCurrentState == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0; 1993 int currentVol = 1994 mSystemInterface 1995 .getAudioManager() 1996 .getStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO); 1997 if (volume != currentVol) { 1998 mSystemInterface 1999 .getAudioManager() 2000 .setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag); 2001 } 2002 } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) { 2003 // Not used currently 2004 mMicVolume = volume; 2005 } else { 2006 Log.e(TAG, "Bad volume type: " + volumeType); 2007 } 2008 } 2009 processNoiseReductionEvent(boolean enable)2010 private void processNoiseReductionEvent(boolean enable) { 2011 log("processNoiseReductionEvent: " + mHasNrecEnabled + " -> " + enable); 2012 mHasNrecEnabled = enable; 2013 if (getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 2014 setAudioParameters(); 2015 } 2016 } 2017 processWBSEvent(int wbsConfig)2018 private void processWBSEvent(int wbsConfig) { 2019 boolean prevWbs = mHasWbsEnabled; 2020 switch (wbsConfig) { 2021 case HeadsetHalConstants.BTHF_WBS_YES: 2022 mHasWbsEnabled = true; 2023 if (Flags.hfpCodecAptxVoice() && mHeadsetService.isAptXSwbEnabled()) { 2024 mHasSwbAptXEnabled = false; 2025 } 2026 break; 2027 case HeadsetHalConstants.BTHF_WBS_NO: 2028 case HeadsetHalConstants.BTHF_WBS_NONE: 2029 mHasWbsEnabled = false; 2030 break; 2031 default: 2032 Log.e(TAG, "processWBSEvent: unknown wbsConfig " + wbsConfig); 2033 return; 2034 } 2035 log("processWBSEvent: " + prevWbs + " -> " + mHasWbsEnabled); 2036 } 2037 processSWBEvent(int swbCodec, int swbConfig)2038 private void processSWBEvent(int swbCodec, int swbConfig) { 2039 boolean prevSwbLc3 = mHasSwbLc3Enabled; 2040 boolean prevSwbAptx = mHasSwbAptXEnabled; 2041 boolean success = true; 2042 2043 switch (swbConfig) { 2044 case HeadsetHalConstants.BTHF_SWB_YES: 2045 switch (swbCodec) { 2046 case HeadsetHalConstants.BTHF_SWB_CODEC_LC3: 2047 mHasSwbLc3Enabled = true; 2048 mHasWbsEnabled = false; 2049 mHasSwbAptXEnabled = false; 2050 break; 2051 case HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX: 2052 mHasSwbLc3Enabled = false; 2053 mHasWbsEnabled = false; 2054 mHasSwbAptXEnabled = true; 2055 break; 2056 default: 2057 success = false; 2058 break; 2059 } 2060 break; 2061 case HeadsetHalConstants.BTHF_SWB_NO: 2062 case HeadsetHalConstants.BTHF_SWB_NONE: 2063 mHasSwbLc3Enabled = false; 2064 mHasSwbAptXEnabled = false; 2065 break; 2066 default: 2067 success = false; 2068 } 2069 2070 if (!success) { 2071 Log.e( 2072 TAG, 2073 ("processSWBEvent failed: swbCodec: " + swbCodec) 2074 + (" swb_config: " + swbConfig)); 2075 return; 2076 } 2077 2078 log("processSWBEvent LC3 SWB config: " + prevSwbLc3 + " -> " + mHasSwbLc3Enabled); 2079 log("processSWBEvent AptX SWB config: " + prevSwbAptx + " -> " + mHasSwbAptXEnabled); 2080 } 2081 2082 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) 2083 @VisibleForTesting processAtChld(int chld, BluetoothDevice device)2084 void processAtChld(int chld, BluetoothDevice device) { 2085 if (mSystemInterface.processChld(chld)) { 2086 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 2087 } else { 2088 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2089 } 2090 } 2091 2092 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) 2093 @VisibleForTesting processSubscriberNumberRequest(BluetoothDevice device)2094 void processSubscriberNumberRequest(BluetoothDevice device) { 2095 String number = mSystemInterface.getSubscriberNumber(); 2096 if (number != null) { 2097 mNativeInterface.atResponseString( 2098 device, 2099 "+CNUM: ,\"" + number + "\"," + PhoneNumberUtils.toaFromString(number) + ",,4"); 2100 } else { 2101 Log.e(TAG, "getSubscriberNumber returns null, no subscriber number can reply"); 2102 } 2103 2104 // Based on spec, if subscriber number is empty, we should still return OK response. 2105 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 2106 } 2107 processAtCind(BluetoothDevice device)2108 private void processAtCind(BluetoothDevice device) { 2109 logi("processAtCind: for device=" + device); 2110 final HeadsetPhoneState phoneState = mSystemInterface.getHeadsetPhoneState(); 2111 int call, callSetup; 2112 int service = phoneState.getCindService(), signal = phoneState.getCindSignal(); 2113 2114 /* Handsfree carkits expect that +CIND is properly responded to 2115 Hence we ensure that a proper response is sent 2116 for the virtual call too.*/ 2117 if (mHeadsetService.isVirtualCallStarted()) { 2118 call = 1; 2119 callSetup = 0; 2120 } else { 2121 // regular phone call 2122 call = phoneState.getNumActiveCall(); 2123 callSetup = phoneState.getNumHeldCall(); 2124 } 2125 2126 if (Flags.pretendNetworkService()) { 2127 logd("processAtCind: pretendNetworkService enabled"); 2128 boolean isCallOngoing = 2129 (phoneState.getNumActiveCall() > 0) 2130 || (phoneState.getNumHeldCall() > 0) 2131 || phoneState.getCallState() == HeadsetHalConstants.CALL_STATE_ALERTING 2132 || phoneState.getCallState() == HeadsetHalConstants.CALL_STATE_DIALING 2133 || phoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING; 2134 if ((isCallOngoing 2135 && (!mHeadsetService.isVirtualCallStarted()) 2136 && (phoneState.getCindService() 2137 == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE))) { 2138 logi( 2139 "processAtCind: If regular call is in progress/active/held while no network" 2140 + " during BT-ON, pretend service availability and signal strength"); 2141 service = HeadsetHalConstants.NETWORK_STATE_AVAILABLE; 2142 signal = 3; 2143 } else { 2144 service = phoneState.getCindService(); 2145 signal = phoneState.getCindSignal(); 2146 } 2147 } 2148 mNativeInterface.cindResponse( 2149 device, 2150 service, 2151 call, 2152 callSetup, 2153 phoneState.getCallState(), 2154 signal, 2155 phoneState.getCindRoam(), 2156 phoneState.getCindBatteryCharge()); 2157 } 2158 2159 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) 2160 @VisibleForTesting processAtCops(BluetoothDevice device)2161 void processAtCops(BluetoothDevice device) { 2162 // Get operator name suggested by Telephony 2163 String operatorName = null; 2164 ServiceState serviceState = mSystemInterface.getHeadsetPhoneState().getServiceState(); 2165 if (serviceState != null) { 2166 operatorName = serviceState.getOperatorAlphaLong(); 2167 if (TextUtils.isEmpty(operatorName)) { 2168 operatorName = serviceState.getOperatorAlphaShort(); 2169 } 2170 } 2171 if (mSystemInterface.isInCall() || TextUtils.isEmpty(operatorName)) { 2172 // Get operator name suggested by Telecom 2173 operatorName = mSystemInterface.getNetworkOperator(); 2174 } 2175 if (operatorName == null) { 2176 operatorName = ""; 2177 } 2178 mNativeInterface.copsResponse(device, operatorName); 2179 } 2180 2181 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) 2182 @VisibleForTesting processAtClcc(BluetoothDevice device)2183 void processAtClcc(BluetoothDevice device) { 2184 if (mHeadsetService.isVirtualCallStarted()) { 2185 // In virtual call, send our phone number instead of remote phone number 2186 String phoneNumber = mSystemInterface.getSubscriberNumber(); 2187 if (phoneNumber == null) { 2188 phoneNumber = ""; 2189 } 2190 int type = PhoneNumberUtils.toaFromString(phoneNumber); 2191 mNativeInterface.clccResponse(device, 1, 0, 0, 0, false, phoneNumber, type); 2192 mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0); 2193 } else { 2194 // In Telecom call, ask Telecom to send send remote phone number 2195 if (!mSystemInterface.listCurrentCalls()) { 2196 Log.e(TAG, "processAtClcc: failed to list current calls for " + device); 2197 mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0); 2198 } else { 2199 sendMessageDelayed(CLCC_RSP_TIMEOUT, device, CLCC_RSP_TIMEOUT_MS); 2200 } 2201 } 2202 } 2203 2204 @VisibleForTesting processAtCscs(String atString, int type, BluetoothDevice device)2205 void processAtCscs(String atString, int type, BluetoothDevice device) { 2206 log("processAtCscs - atString = " + atString); 2207 if (mPhonebook != null) { 2208 mPhonebook.handleCscsCommand(atString, type, device); 2209 } else { 2210 Log.e(TAG, "Phonebook handle null for At+CSCS"); 2211 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2212 } 2213 } 2214 2215 @VisibleForTesting processAtCpbs(String atString, int type, BluetoothDevice device)2216 void processAtCpbs(String atString, int type, BluetoothDevice device) { 2217 log("processAtCpbs - atString = " + atString); 2218 if (mPhonebook != null) { 2219 mPhonebook.handleCpbsCommand(atString, type, device); 2220 } else { 2221 Log.e(TAG, "Phonebook handle null for At+CPBS"); 2222 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2223 } 2224 } 2225 2226 @VisibleForTesting processAtCpbr(String atString, int type, BluetoothDevice device)2227 void processAtCpbr(String atString, int type, BluetoothDevice device) { 2228 log("processAtCpbr - atString = " + atString); 2229 if (mPhonebook != null) { 2230 mPhonebook.handleCpbrCommand(atString, type, device); 2231 } else { 2232 Log.e(TAG, "Phonebook handle null for At+CPBR"); 2233 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2234 } 2235 } 2236 2237 /** Find a character ch, ignoring quoted sections. Return input.length() if not found. */ 2238 @VisibleForTesting findChar(char ch, String input, int fromIndex)2239 static int findChar(char ch, String input, int fromIndex) { 2240 for (int i = fromIndex; i < input.length(); i++) { 2241 char c = input.charAt(i); 2242 if (c == '"') { 2243 i = input.indexOf('"', i + 1); 2244 if (i == -1) { 2245 return input.length(); 2246 } 2247 } else if (c == ch) { 2248 return i; 2249 } 2250 } 2251 return input.length(); 2252 } 2253 2254 /** 2255 * Break an argument string into individual arguments (comma delimited). Integer arguments are 2256 * turned into Integer objects. Otherwise a String object is used. 2257 */ 2258 @VisibleForTesting generateArgs(String input)2259 static Object[] generateArgs(String input) { 2260 int i = 0; 2261 int j; 2262 ArrayList<Object> out = new ArrayList<Object>(); 2263 while (i <= input.length()) { 2264 j = findChar(',', input, i); 2265 2266 String arg = input.substring(i, j); 2267 try { 2268 out.add(Integer.valueOf(arg)); 2269 } catch (NumberFormatException e) { 2270 out.add(arg); 2271 } 2272 2273 i = j + 1; // move past comma 2274 } 2275 return out.toArray(); 2276 } 2277 2278 /** 2279 * Process vendor specific AT commands 2280 * 2281 * @param atString AT command after the "AT+" prefix 2282 * @param device Remote device that has sent this command 2283 */ 2284 @VisibleForTesting processVendorSpecificAt(String atString, BluetoothDevice device)2285 void processVendorSpecificAt(String atString, BluetoothDevice device) { 2286 log("processVendorSpecificAt - atString = " + atString); 2287 2288 // Currently we accept only SET type commands, except the 4 AT commands 2289 // which requests the device's information: +CGMI, +CGMM, +CGMR and +CGSN, which we 2290 // responds to right away without any further processing. 2291 boolean isIopInfoRequestAt = true; 2292 switch (atString) { 2293 case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGMI: 2294 processAtCgmi(device); 2295 break; 2296 case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGMM: 2297 processAtCgmm(device); 2298 break; 2299 case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGMR: 2300 processAtCgmr(device); 2301 break; 2302 case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_CGSN: 2303 processAtCgsn(device); 2304 break; 2305 default: 2306 isIopInfoRequestAt = false; 2307 } 2308 if (isIopInfoRequestAt) { 2309 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 2310 return; 2311 } 2312 2313 // Check if the command is a SET type command. 2314 int indexOfEqual = atString.indexOf("="); 2315 if (indexOfEqual == -1) { 2316 Log.w(TAG, "processVendorSpecificAt: command type error in " + atString); 2317 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2318 return; 2319 } 2320 2321 String command = atString.substring(0, indexOfEqual); 2322 Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command); 2323 if (companyId == null) { 2324 Log.i(TAG, "processVendorSpecificAt: unsupported command: " + atString); 2325 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2326 return; 2327 } 2328 2329 String arg = atString.substring(indexOfEqual + 1); 2330 if (arg.startsWith("?")) { 2331 Log.w(TAG, "processVendorSpecificAt: command type error in " + atString); 2332 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2333 return; 2334 } 2335 2336 Object[] args = generateArgs(arg); 2337 if (command.equals(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XAPL)) { 2338 processAtXapl(args, device); 2339 } 2340 broadcastVendorSpecificEventIntent( 2341 command, companyId, BluetoothHeadset.AT_CMD_TYPE_SET, args, device); 2342 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 2343 } 2344 2345 /** 2346 * Look for Android specific AT command starts with AT+ANDROID and try to process it 2347 * 2348 * @param atString AT command in string 2349 * @param device Remote device that has sent this command 2350 * @return true if the command is processed, false if not. 2351 */ 2352 @VisibleForTesting checkAndProcessAndroidAt(String atString, BluetoothDevice device)2353 boolean checkAndProcessAndroidAt(String atString, BluetoothDevice device) { 2354 log("checkAndProcessAndroidAt - atString = " + atString); 2355 2356 if (atString.equals("+ANDROID=?")) { 2357 // feature request type command 2358 processAndroidAtFeatureRequest(device); 2359 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 2360 return true; 2361 } else if (atString.startsWith("+ANDROID=")) { 2362 // set type command 2363 int equalIndex = atString.indexOf("="); 2364 String arg = atString.substring(equalIndex + 1); 2365 2366 if (arg.isEmpty()) { 2367 Log.w(TAG, "Android AT command is empty"); 2368 return false; 2369 } 2370 2371 Object[] args = generateArgs(arg); 2372 2373 if (!(args[0] instanceof String)) { 2374 Log.w(TAG, "Incorrect type of Android AT command!"); 2375 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2376 return true; 2377 } 2378 2379 String type = (String) args[0]; 2380 2381 if (type.equals(BluetoothSinkAudioPolicy.HFP_SET_SINK_AUDIO_POLICY_ID)) { 2382 Log.d(TAG, "Processing command: " + atString); 2383 if (processAndroidAtSinkAudioPolicy(args, device)) { 2384 mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0); 2385 } else { 2386 Log.w(TAG, "Invalid SinkAudioPolicy parameters!"); 2387 mNativeInterface.atResponseCode( 2388 device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 2389 } 2390 return true; 2391 } else { 2392 Log.w(TAG, "Undefined Android command type: " + type); 2393 return false; 2394 } 2395 } 2396 2397 Log.w(TAG, "Unhandled +ANDROID command: " + atString); 2398 return false; 2399 } 2400 processAndroidAtFeatureRequest(BluetoothDevice device)2401 private void processAndroidAtFeatureRequest(BluetoothDevice device) { 2402 /* 2403 replying with +ANDROID: (<feature1>, <feature2>, ...) 2404 currently we only support one type of feature: SINKAUDIOPOLICY 2405 */ 2406 mNativeInterface.atResponseString( 2407 device, 2408 BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID 2409 + ": (" 2410 + BluetoothSinkAudioPolicy.HFP_SET_SINK_AUDIO_POLICY_ID 2411 + ")"); 2412 } 2413 2414 /** 2415 * Process AT+ANDROID=SINKAUDIOPOLICY AT command 2416 * 2417 * @param args command arguments after the equal sign 2418 * @param device Remote device that has sent this command 2419 * @return true on success, false on error 2420 */ 2421 @VisibleForTesting processAndroidAtSinkAudioPolicy(Object[] args, BluetoothDevice device)2422 boolean processAndroidAtSinkAudioPolicy(Object[] args, BluetoothDevice device) { 2423 if (args.length != 4) { 2424 Log.e( 2425 TAG, 2426 "processAndroidAtSinkAudioPolicy() args length must be 4: " 2427 + String.valueOf(args.length)); 2428 return false; 2429 } 2430 if (!(args[1] instanceof Integer) 2431 || !(args[2] instanceof Integer) 2432 || !(args[3] instanceof Integer)) { 2433 Log.e(TAG, "processAndroidAtSinkAudioPolicy() argument types not matched"); 2434 return false; 2435 } 2436 2437 if (!mDevice.equals(device)) { 2438 Log.e( 2439 TAG, 2440 "processAndroidAtSinkAudioPolicy(): argument device " 2441 + device 2442 + " doesn't match mDevice " 2443 + mDevice); 2444 return false; 2445 } 2446 2447 int callEstablishPolicy = (Integer) args[1]; 2448 int connectingTimePolicy = (Integer) args[2]; 2449 int inbandPolicy = (Integer) args[3]; 2450 2451 setHfpCallAudioPolicy( 2452 new BluetoothSinkAudioPolicy.Builder() 2453 .setCallEstablishPolicy(callEstablishPolicy) 2454 .setActiveDevicePolicyAfterConnection(connectingTimePolicy) 2455 .setInBandRingtonePolicy(inbandPolicy) 2456 .build()); 2457 return true; 2458 } 2459 2460 /** 2461 * sets the audio policy of the client device and stores in the database 2462 * 2463 * @param policies policies to be set and stored 2464 */ setHfpCallAudioPolicy(BluetoothSinkAudioPolicy policies)2465 public void setHfpCallAudioPolicy(BluetoothSinkAudioPolicy policies) { 2466 mHsClientAudioPolicy = policies; 2467 mDatabaseManager.setAudioPolicyMetadata(mDevice, policies); 2468 } 2469 2470 /** get the audio policy of the client device */ getHfpCallAudioPolicy()2471 public BluetoothSinkAudioPolicy getHfpCallAudioPolicy() { 2472 return mHsClientAudioPolicy; 2473 } 2474 2475 /** 2476 * Process AT+XAPL AT command 2477 * 2478 * @param args command arguments after the equal sign 2479 * @param device Remote device that has sent this command 2480 */ 2481 @VisibleForTesting processAtXapl(Object[] args, BluetoothDevice device)2482 void processAtXapl(Object[] args, BluetoothDevice device) { 2483 if (args.length != 2) { 2484 Log.w(TAG, "processAtXapl() args length must be 2: " + String.valueOf(args.length)); 2485 return; 2486 } 2487 if (!(args[0] instanceof String) || !(args[1] instanceof Integer)) { 2488 Log.w(TAG, "processAtXapl() argument types not match"); 2489 return; 2490 } 2491 String[] deviceInfo = ((String) args[0]).split("-"); 2492 if (deviceInfo.length != 3) { 2493 Log.w(TAG, "processAtXapl() deviceInfo length " + deviceInfo.length + " is wrong"); 2494 return; 2495 } 2496 String vendorId = deviceInfo[0]; 2497 String productId = deviceInfo[1]; 2498 String version = deviceInfo[2]; 2499 String[] macAddress = device.getAddress().split(":"); 2500 BluetoothStatsLog.write( 2501 BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED, 2502 mAdapterService.obfuscateAddress(device), 2503 BluetoothProtoEnums.DEVICE_INFO_INTERNAL, 2504 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XAPL, 2505 vendorId, 2506 productId, 2507 version, 2508 null, 2509 mAdapterService.getMetricId(device), 2510 device.getAddressType(), 2511 Integer.parseInt(macAddress[0], 16), 2512 Integer.parseInt(macAddress[1], 16), 2513 Integer.parseInt(macAddress[2], 16)); 2514 // feature = 2 indicates that we support battery level reporting only 2515 mNativeInterface.atResponseString(device, "+XAPL=iPhone," + String.valueOf(2)); 2516 } 2517 2518 /** 2519 * Process AT+CGMI AT command 2520 * 2521 * @param device Remote device that has sent this command 2522 */ 2523 @VisibleForTesting processAtCgmi(BluetoothDevice device)2524 void processAtCgmi(BluetoothDevice device) { 2525 mNativeInterface.atResponseString(device, Build.MANUFACTURER); 2526 } 2527 2528 /** 2529 * Process AT+CGMM AT command 2530 * 2531 * @param device Remote device that has sent this command 2532 */ 2533 @VisibleForTesting processAtCgmm(BluetoothDevice device)2534 void processAtCgmm(BluetoothDevice device) { 2535 mNativeInterface.atResponseString(device, Build.MODEL); 2536 } 2537 2538 /** 2539 * Process AT+CGMR AT command 2540 * 2541 * @param device Remote device that has sent this command 2542 */ 2543 @VisibleForTesting processAtCgmr(BluetoothDevice device)2544 void processAtCgmr(BluetoothDevice device) { 2545 mNativeInterface.atResponseString( 2546 device, String.format("%s (%s)", Build.VERSION.RELEASE, Build.VERSION.INCREMENTAL)); 2547 } 2548 2549 /** 2550 * Process AT+CGSN AT command 2551 * 2552 * @param device Remote device that has sent this command 2553 */ 2554 @VisibleForTesting processAtCgsn(BluetoothDevice device)2555 void processAtCgsn(BluetoothDevice device) { 2556 mNativeInterface.atResponseString(device, Build.getSerial()); 2557 } 2558 2559 @VisibleForTesting processUnknownAt(String atString, BluetoothDevice device)2560 void processUnknownAt(String atString, BluetoothDevice device) { 2561 if (device == null) { 2562 Log.w(TAG, "processUnknownAt device is null"); 2563 return; 2564 } 2565 log("processUnknownAt - atString = " + atString); 2566 String atCommand = parseUnknownAt(atString); 2567 int commandType = getAtCommandType(atCommand); 2568 if (atCommand.startsWith("+CSCS")) { 2569 processAtCscs(atCommand.substring(5), commandType, device); 2570 } else if (atCommand.startsWith("+CPBS")) { 2571 processAtCpbs(atCommand.substring(5), commandType, device); 2572 } else if (atCommand.startsWith("+CPBR")) { 2573 processAtCpbr(atCommand.substring(5), commandType, device); 2574 } else if (atCommand.startsWith("+ANDROID") 2575 && checkAndProcessAndroidAt(atCommand, device)) { 2576 // Do nothing 2577 } else { 2578 processVendorSpecificAt(atCommand, device); 2579 } 2580 } 2581 2582 // HSP +CKPD command 2583 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) processKeyPressed(BluetoothDevice device)2584 private void processKeyPressed(BluetoothDevice device) { 2585 if (mSystemInterface.isRinging()) { 2586 mSystemInterface.answerCall(device); 2587 } else if (mSystemInterface.isInCall()) { 2588 if (getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 2589 // Should connect audio as well 2590 if (!mHeadsetService.setActiveDevice(mDevice)) { 2591 Log.w(TAG, "processKeyPressed, failed to set active device to " + mDevice); 2592 } 2593 } else { 2594 mSystemInterface.hangupCall(device); 2595 } 2596 } else if (getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 2597 if (!mNativeInterface.disconnectAudio(mDevice)) { 2598 Log.w(TAG, "processKeyPressed, failed to disconnect audio from " + mDevice); 2599 } 2600 } else { 2601 // We have already replied OK to this HSP command, no feedback is needed 2602 if (mHeadsetService.hasDeviceInitiatedDialingOut()) { 2603 Log.w(TAG, "processKeyPressed, already dialling"); 2604 return; 2605 } 2606 String dialNumber = mPhonebook.getLastDialledNumber(); 2607 if (dialNumber == null) { 2608 Log.w(TAG, "processKeyPressed, last dial number null"); 2609 return; 2610 } 2611 if (!mHeadsetService.dialOutgoingCall(mDevice, dialNumber)) { 2612 Log.w(TAG, "processKeyPressed, failed to call in service"); 2613 return; 2614 } 2615 } 2616 } 2617 2618 /** 2619 * Send HF indicator value changed intent 2620 * 2621 * @param device Device whose HF indicator value has changed 2622 * @param indId Indicator ID [0-65535] 2623 * @param indValue Indicator Value [0-65535], -1 means invalid but indId is supported 2624 */ sendIndicatorIntent(BluetoothDevice device, int indId, int indValue)2625 private void sendIndicatorIntent(BluetoothDevice device, int indId, int indValue) { 2626 mAdapterService.getRemoteDevices().handleHfIndicatorValueChanged(device, indId, indValue); 2627 Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED); 2628 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2629 intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indId); 2630 intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, indValue); 2631 mHeadsetService.sendBroadcast( 2632 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 2633 } 2634 processAtBind(String atString, BluetoothDevice device)2635 private void processAtBind(String atString, BluetoothDevice device) { 2636 log("processAtBind: " + atString); 2637 2638 for (String id : atString.split(",")) { 2639 2640 int indId; 2641 2642 try { 2643 indId = Integer.parseInt(id); 2644 } catch (NumberFormatException e) { 2645 Log.e(TAG, Log.getStackTraceString(new Throwable())); 2646 continue; 2647 } 2648 2649 switch (indId) { 2650 case HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY: 2651 log("Send Broadcast intent for the Enhanced Driver Safety indicator."); 2652 sendIndicatorIntent(device, indId, -1); 2653 break; 2654 case HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS: 2655 log("Send Broadcast intent for the Battery Level indicator."); 2656 sendIndicatorIntent(device, indId, -1); 2657 break; 2658 default: 2659 log("Invalid HF Indicator Received"); 2660 break; 2661 } 2662 } 2663 } 2664 2665 @VisibleForTesting processAtBiev(int indId, int indValue, BluetoothDevice device)2666 void processAtBiev(int indId, int indValue, BluetoothDevice device) { 2667 log("processAtBiev: ind_id=" + indId + ", ind_value=" + indValue); 2668 sendIndicatorIntent(device, indId, indValue); 2669 } 2670 2671 @VisibleForTesting processSendClccResponse(HeadsetClccResponse clcc)2672 void processSendClccResponse(HeadsetClccResponse clcc) { 2673 if (!hasMessages(CLCC_RSP_TIMEOUT)) { 2674 return; 2675 } 2676 if (clcc.mIndex == 0) { 2677 removeMessages(CLCC_RSP_TIMEOUT); 2678 } 2679 mNativeInterface.clccResponse( 2680 mDevice, 2681 clcc.mIndex, 2682 clcc.mDirection, 2683 clcc.mStatus, 2684 clcc.mMode, 2685 clcc.mMpty, 2686 clcc.mNumber, 2687 clcc.mType); 2688 } 2689 2690 @VisibleForTesting processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode)2691 void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) { 2692 String stringToSend = resultCode.mCommand + ": "; 2693 if (resultCode.mArg != null) { 2694 stringToSend += resultCode.mArg; 2695 } 2696 mNativeInterface.atResponseString(resultCode.mDevice, stringToSend); 2697 } 2698 getCurrentDeviceName()2699 private String getCurrentDeviceName() { 2700 String deviceName = mAdapterService.getRemoteName(mDevice); 2701 if (deviceName == null) { 2702 return "<unknown>"; 2703 } 2704 return deviceName; 2705 } 2706 updateAgIndicatorEnableState( HeadsetAgIndicatorEnableState agIndicatorEnableState)2707 private void updateAgIndicatorEnableState( 2708 HeadsetAgIndicatorEnableState agIndicatorEnableState) { 2709 if (!mDeviceSilenced && Objects.equals(mAgIndicatorEnableState, agIndicatorEnableState)) { 2710 Log.i( 2711 TAG, 2712 "updateAgIndicatorEnableState, no change in indicator state " 2713 + mAgIndicatorEnableState); 2714 return; 2715 } 2716 mAgIndicatorEnableState = agIndicatorEnableState; 2717 int events = PhoneStateListener.LISTEN_NONE; 2718 if (mAgIndicatorEnableState != null && mAgIndicatorEnableState.service) { 2719 events |= PhoneStateListener.LISTEN_SERVICE_STATE; 2720 } 2721 if (mAgIndicatorEnableState != null && mAgIndicatorEnableState.signal) { 2722 events |= PhoneStateListener.LISTEN_SIGNAL_STRENGTHS; 2723 } 2724 mSystemInterface.getHeadsetPhoneState().listenForPhoneState(mDevice, events); 2725 } 2726 2727 @Override log(String msg)2728 protected void log(String msg) { 2729 super.log(msg); 2730 } 2731 2732 @Override getLogRecString(Message msg)2733 protected String getLogRecString(Message msg) { 2734 StringBuilder builder = new StringBuilder(); 2735 builder.append(getMessageName(msg.what)); 2736 builder.append(": "); 2737 builder.append("arg1=") 2738 .append(msg.arg1) 2739 .append(", arg2=") 2740 .append(msg.arg2) 2741 .append(", obj="); 2742 if (msg.obj instanceof HeadsetMessageObject) { 2743 HeadsetMessageObject object = (HeadsetMessageObject) msg.obj; 2744 object.buildString(builder); 2745 } else { 2746 builder.append(msg.obj); 2747 } 2748 return builder.toString(); 2749 } 2750 2751 @VisibleForTesting handleAccessPermissionResult(Intent intent)2752 void handleAccessPermissionResult(Intent intent) { 2753 log("handleAccessPermissionResult"); 2754 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2755 if (!mPhonebook.getCheckingAccessPermission()) { 2756 return; 2757 } 2758 int atCommandResult = 0; 2759 int atCommandErrorCode = 0; 2760 // HeadsetBase headset = mHandsfree.getHeadset(); 2761 // ASSERT: (headset != null) && headSet.isConnected() 2762 // REASON: mCheckingAccessPermission is true, otherwise resetAtState 2763 // has set mCheckingAccessPermission to false 2764 if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 2765 if (intent.getIntExtra( 2766 BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 2767 BluetoothDevice.CONNECTION_ACCESS_NO) 2768 == BluetoothDevice.CONNECTION_ACCESS_YES) { 2769 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 2770 mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); 2771 } 2772 atCommandResult = mPhonebook.processCpbrCommand(device); 2773 } else { 2774 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 2775 mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); 2776 } 2777 } 2778 } 2779 mPhonebook.setCpbrIndex(-1); 2780 mPhonebook.setCheckingAccessPermission(false); 2781 if (atCommandResult >= 0) { 2782 mNativeInterface.atResponseCode(device, atCommandResult, atCommandErrorCode); 2783 } else { 2784 log("handleAccessPermissionResult - RESULT_NONE"); 2785 } 2786 } 2787 getConnectionStateFromAudioState(int audioState)2788 private static int getConnectionStateFromAudioState(int audioState) { 2789 switch (audioState) { 2790 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 2791 return BluetoothAdapter.STATE_CONNECTED; 2792 case BluetoothHeadset.STATE_AUDIO_CONNECTING: 2793 return BluetoothAdapter.STATE_CONNECTING; 2794 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: 2795 return BluetoothAdapter.STATE_DISCONNECTED; 2796 } 2797 return BluetoothAdapter.STATE_DISCONNECTED; 2798 } 2799 getMessageName(int what)2800 private static String getMessageName(int what) { 2801 switch (what) { 2802 case CONNECT: 2803 return "CONNECT"; 2804 case DISCONNECT: 2805 return "DISCONNECT"; 2806 case CONNECT_AUDIO: 2807 return "CONNECT_AUDIO"; 2808 case DISCONNECT_AUDIO: 2809 return "DISCONNECT_AUDIO"; 2810 case VOICE_RECOGNITION_START: 2811 return "VOICE_RECOGNITION_START"; 2812 case VOICE_RECOGNITION_STOP: 2813 return "VOICE_RECOGNITION_STOP"; 2814 case INTENT_SCO_VOLUME_CHANGED: 2815 return "INTENT_SCO_VOLUME_CHANGED"; 2816 case INTENT_CONNECTION_ACCESS_REPLY: 2817 return "INTENT_CONNECTION_ACCESS_REPLY"; 2818 case CALL_STATE_CHANGED: 2819 return "CALL_STATE_CHANGED"; 2820 case DEVICE_STATE_CHANGED: 2821 return "DEVICE_STATE_CHANGED"; 2822 case SEND_CLCC_RESPONSE: 2823 return "SEND_CLCC_RESPONSE"; 2824 case SEND_VENDOR_SPECIFIC_RESULT_CODE: 2825 return "SEND_VENDOR_SPECIFIC_RESULT_CODE"; 2826 case STACK_EVENT: 2827 return "STACK_EVENT"; 2828 case VOICE_RECOGNITION_RESULT: 2829 return "VOICE_RECOGNITION_RESULT"; 2830 case DIALING_OUT_RESULT: 2831 return "DIALING_OUT_RESULT"; 2832 case CLCC_RSP_TIMEOUT: 2833 return "CLCC_RSP_TIMEOUT"; 2834 case CONNECT_TIMEOUT: 2835 return "CONNECT_TIMEOUT"; 2836 default: 2837 return "UNKNOWN(" + what + ")"; 2838 } 2839 } 2840 } 2841