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