• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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