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