• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 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 // Bluetooth Headset Client State Machine
18 //                (Disconnected)
19 //                  |        ^
20 //          CONNECT |        | DISCONNECTED
21 //                  V        |
22 //          (Connecting)   (Disconnecting)
23 //                  |        ^
24 //        CONNECTED |        | DISCONNECT
25 //                  V        |
26 //                  (Connected)
27 //                  |        |
28 //    CONNECT_AUDIO |        | DISCONNECT_AUDIO
29 //                  |        |
30 //                  (Audio_On)
31 
32 package com.android.bluetooth.hfpclient;
33 
34 import static android.Manifest.permission.BLUETOOTH_CONNECT;
35 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
36 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
37 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
38 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
39 import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
40 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
41 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
42 import static android.content.pm.PackageManager.FEATURE_WATCH;
43 
44 import static java.util.Objects.requireNonNull;
45 
46 import android.bluetooth.BluetoothAdapter;
47 import android.bluetooth.BluetoothDevice;
48 import android.bluetooth.BluetoothHeadsetClient;
49 import android.bluetooth.BluetoothHeadsetClient.NetworkServiceState;
50 import android.bluetooth.BluetoothProfile;
51 import android.bluetooth.BluetoothSinkAudioPolicy;
52 import android.bluetooth.BluetoothStatusCodes;
53 import android.bluetooth.BluetoothUuid;
54 import android.bluetooth.hfp.BluetoothHfpProtoEnums;
55 import android.content.Intent;
56 import android.media.AudioAttributes;
57 import android.media.AudioFocusRequest;
58 import android.media.AudioManager;
59 import android.os.Bundle;
60 import android.os.Looper;
61 import android.os.Message;
62 import android.os.ParcelUuid;
63 import android.os.SystemClock;
64 import android.os.SystemProperties;
65 import android.util.Log;
66 import android.util.Pair;
67 
68 import com.android.bluetooth.BluetoothStatsLog;
69 import com.android.bluetooth.R;
70 import com.android.bluetooth.Utils;
71 import com.android.bluetooth.btservice.AdapterService;
72 import com.android.bluetooth.btservice.MetricsLogger;
73 import com.android.bluetooth.btservice.ProfileService;
74 import com.android.bluetooth.flags.Flags;
75 import com.android.bluetooth.hfp.HeadsetService;
76 import com.android.internal.annotations.VisibleForTesting;
77 import com.android.internal.util.IState;
78 import com.android.internal.util.State;
79 import com.android.internal.util.StateMachine;
80 
81 import java.io.FileDescriptor;
82 import java.io.PrintWriter;
83 import java.io.StringWriter;
84 import java.util.ArrayDeque;
85 import java.util.ArrayList;
86 import java.util.Arrays;
87 import java.util.HashSet;
88 import java.util.List;
89 import java.util.Map;
90 import java.util.Scanner;
91 import java.util.Set;
92 import java.util.concurrent.ConcurrentHashMap;
93 
94 public class HeadsetClientStateMachine extends StateMachine {
95     private static final String TAG = HeadsetClientStateMachine.class.getSimpleName();
96 
97     static final int NO_ACTION = 0;
98     static final int IN_BAND_RING_ENABLED = 1;
99 
100     // external actions
101     public static final int AT_OK = 0;
102     public static final int CONNECT = 1;
103     public static final int DISCONNECT = 2;
104     public static final int CONNECT_AUDIO = 3;
105     public static final int DISCONNECT_AUDIO = 4;
106     public static final int VOICE_RECOGNITION_START = 5;
107     public static final int VOICE_RECOGNITION_STOP = 6;
108     public static final int SET_MIC_VOLUME = 7;
109     public static final int SET_SPEAKER_VOLUME = 8;
110     public static final int DIAL_NUMBER = 10;
111     public static final int ACCEPT_CALL = 12;
112     public static final int REJECT_CALL = 13;
113     public static final int HOLD_CALL = 14;
114     public static final int TERMINATE_CALL = 15;
115     public static final int ENTER_PRIVATE_MODE = 16;
116     public static final int SEND_DTMF = 17;
117     public static final int EXPLICIT_CALL_TRANSFER = 18;
118     public static final int DISABLE_NREC = 20;
119     public static final int SEND_VENDOR_AT_COMMAND = 21;
120     public static final int SEND_BIEV = 22;
121     public static final int SEND_ANDROID_AT_COMMAND = 23;
122 
123     // internal actions
124     @VisibleForTesting static final int QUERY_CURRENT_CALLS = 50;
125     @VisibleForTesting static final int QUERY_OPERATOR_NAME = 51;
126     @VisibleForTesting static final int SUBSCRIBER_INFO = 52;
127     @VisibleForTesting static final int CONNECTING_TIMEOUT = 53;
128     @VisibleForTesting static final int DISCONNECTING_TIMEOUT = 54;
129 
130     // special action to handle terminating specific call from multiparty call
131     static final int TERMINATE_SPECIFIC_CALL = 53;
132 
133     // Timeouts.
134     @VisibleForTesting static final int CONNECTING_TIMEOUT_MS = 10000; // 10s
135     @VisibleForTesting static final int DISCONNECTING_TIMEOUT_MS = 10000; // 10s
136     private static final int ROUTING_DELAY_MS = 250;
137 
138     static final int HF_ORIGINATED_CALL_ID = -1;
139     private static final long OUTGOING_TIMEOUT_MILLI = 10 * 1000; // 10 seconds
140     private static final long QUERY_CURRENT_CALLS_WAIT_MILLIS = 2 * 1000; // 2 seconds
141 
142     // Keep track of audio routing across all devices.
143     private static boolean sAudioIsRouted = false;
144 
145     private final Disconnected mDisconnected;
146     private final Connecting mConnecting;
147     private final Connected mConnected;
148     private final Disconnecting mDisconnecting;
149     private final AudioOn mAudioOn;
150     private State mPrevState;
151 
152     private final AdapterService mAdapterService;
153     private final HeadsetClientService mService;
154     private final HeadsetService mHeadsetService;
155 
156     // Set of calls that represent the accurate state of calls that exists on AG and the calls that
157     // are currently in process of being notified to the AG from HF.
158     @VisibleForTesting final Map<Integer, HfpClientCall> mCalls = new ConcurrentHashMap<>();
159     // Set of calls received from AG via the AT+CLCC command. We use this map to update the mCalls
160     // which is eventually used to inform the telephony stack of any changes to call on HF.
161     private final Map<Integer, HfpClientCall> mCallsUpdate = new ConcurrentHashMap<>();
162 
163     private int mIndicatorNetworkState;
164     private int mIndicatorNetworkType;
165     private int mIndicatorNetworkSignal;
166     private int mIndicatorBatteryLevel;
167     private boolean mInBandRing;
168 
169     private String mOperatorName;
170     @VisibleForTesting String mSubscriberInfo;
171 
172     // queue of send actions (pair action, action_data)
173     @VisibleForTesting ArrayDeque<Pair<Integer, Object>> mQueuedActions;
174 
175     @VisibleForTesting int mAudioState;
176     // Indicates whether audio can be routed to the device
177     private boolean mAudioRouteAllowed;
178 
179     private final boolean mClccPollDuringCall;
180 
181     public int mAudioPolicyRemoteSupported;
182     private BluetoothSinkAudioPolicy mHsClientAudioPolicy;
183     private final int mConnectingTimePolicyProperty;
184     private final int mInBandRingtonePolicyProperty;
185     private final boolean mForceSetAudioPolicyProperty;
186 
187     @VisibleForTesting boolean mAudioWbs;
188 
189     @VisibleForTesting boolean mAudioSWB;
190 
191     private int mVoiceRecognitionActive;
192 
193     // currently connected device
194     @VisibleForTesting BluetoothDevice mCurrentDevice = null;
195 
196     // general peer features and call handling features
197     @VisibleForTesting int mPeerFeatures;
198     @VisibleForTesting int mChldFeatures;
199 
200     // This is returned when requesting focus from AudioManager
201     private AudioFocusRequest mAudioFocusRequest;
202 
203     private final AudioManager mAudioManager;
204     private final NativeInterface mNativeInterface;
205     private final VendorCommandResponseProcessor mVendorProcessor;
206 
207     // Accessor for the states, useful for reusing the state machines
getDisconnectedState()208     public IState getDisconnectedState() {
209         return mDisconnected;
210     }
211 
212     // Get if in band ring is currently enabled on device.
getInBandRing()213     public boolean getInBandRing() {
214         return mInBandRing;
215     }
216 
dump(StringBuilder sb)217     public void dump(StringBuilder sb) {
218         if (mCurrentDevice != null) {
219             ProfileService.println(sb, "==== StateMachine for " + mCurrentDevice + " ====");
220             ProfileService.println(
221                     sb,
222                     "  mCurrentDevice: "
223                             + mCurrentDevice
224                             + "("
225                             + Utils.getName(mCurrentDevice)
226                             + ") "
227                             + this.toString());
228         }
229         ProfileService.println(sb, "  mAudioState: " + mAudioState);
230         ProfileService.println(sb, "  mAudioWbs: " + mAudioWbs);
231         ProfileService.println(sb, "  mAudioSWB: " + mAudioSWB);
232         ProfileService.println(sb, "  mIndicatorNetworkState: " + mIndicatorNetworkState);
233         ProfileService.println(sb, "  mIndicatorNetworkType: " + mIndicatorNetworkType);
234         ProfileService.println(sb, "  mIndicatorNetworkSignal: " + mIndicatorNetworkSignal);
235         ProfileService.println(sb, "  mIndicatorBatteryLevel: " + mIndicatorBatteryLevel);
236         ProfileService.println(sb, "  mOperatorName: " + mOperatorName);
237         ProfileService.println(sb, "  mSubscriberInfo: " + mSubscriberInfo);
238         ProfileService.println(sb, "  mAudioRouteAllowed: " + mAudioRouteAllowed);
239         ProfileService.println(sb, "  mAudioPolicyRemoteSupported: " + mAudioPolicyRemoteSupported);
240         ProfileService.println(sb, "  mHsClientAudioPolicy: " + mHsClientAudioPolicy);
241         ProfileService.println(sb, "  mInBandRing: " + mInBandRing);
242 
243         ProfileService.println(sb, "  mCalls:");
244         if (mCalls != null) {
245             for (HfpClientCall call : mCalls.values()) {
246                 ProfileService.println(sb, "    " + call);
247             }
248         }
249 
250         ProfileService.println(sb, "  mCallsUpdate:");
251         if (mCallsUpdate != null) {
252             for (HfpClientCall call : mCallsUpdate.values()) {
253                 ProfileService.println(sb, "    " + call);
254             }
255         }
256 
257         // Dump the state machine logs
258         StringWriter stringWriter = new StringWriter();
259         PrintWriter printWriter = new PrintWriter(stringWriter);
260         super.dump(new FileDescriptor(), printWriter, new String[] {});
261         printWriter.flush();
262         stringWriter.flush();
263         ProfileService.println(sb, "  StateMachineLog:");
264         Scanner scanner = new Scanner(stringWriter.toString());
265         while (scanner.hasNextLine()) {
266             String line = scanner.nextLine();
267             ProfileService.println(sb, "    " + line);
268         }
269     }
270 
271     @Override
getLogRecString(Message msg)272     protected String getLogRecString(Message msg) {
273         StringBuilder builder = new StringBuilder();
274         builder.append(getMessageName(msg.what));
275         builder.append(": ");
276         builder.append("arg1=")
277                 .append(msg.arg1)
278                 .append(", arg2=")
279                 .append(msg.arg2)
280                 .append(", obj=")
281                 .append(msg.obj);
282         return builder.toString();
283     }
284 
285     @VisibleForTesting
getMessageName(int what)286     static String getMessageName(int what) {
287         switch (what) {
288             case StackEvent.STACK_EVENT:
289                 return "STACK_EVENT";
290             case CONNECT:
291                 return "CONNECT";
292             case DISCONNECT:
293                 return "DISCONNECT";
294             case CONNECT_AUDIO:
295                 return "CONNECT_AUDIO";
296             case DISCONNECT_AUDIO:
297                 return "DISCONNECT_AUDIO";
298             case VOICE_RECOGNITION_START:
299                 return "VOICE_RECOGNITION_START";
300             case VOICE_RECOGNITION_STOP:
301                 return "VOICE_RECOGNITION_STOP";
302             case SET_MIC_VOLUME:
303                 return "SET_MIC_VOLUME";
304             case SET_SPEAKER_VOLUME:
305                 return "SET_SPEAKER_VOLUME";
306             case DIAL_NUMBER:
307                 return "DIAL_NUMBER";
308             case ACCEPT_CALL:
309                 return "ACCEPT_CALL";
310             case REJECT_CALL:
311                 return "REJECT_CALL";
312             case HOLD_CALL:
313                 return "HOLD_CALL";
314             case TERMINATE_CALL:
315                 return "TERMINATE_CALL";
316             case ENTER_PRIVATE_MODE:
317                 return "ENTER_PRIVATE_MODE";
318             case SEND_DTMF:
319                 return "SEND_DTMF";
320             case EXPLICIT_CALL_TRANSFER:
321                 return "EXPLICIT_CALL_TRANSFER";
322             case DISABLE_NREC:
323                 return "DISABLE_NREC";
324             case SEND_VENDOR_AT_COMMAND:
325                 return "SEND_VENDOR_AT_COMMAND";
326             case SEND_BIEV:
327                 return "SEND_BIEV";
328             case QUERY_CURRENT_CALLS:
329                 return "QUERY_CURRENT_CALLS";
330             case QUERY_OPERATOR_NAME:
331                 return "QUERY_OPERATOR_NAME";
332             case SUBSCRIBER_INFO:
333                 return "SUBSCRIBER_INFO";
334             case CONNECTING_TIMEOUT:
335                 return "CONNECTING_TIMEOUT";
336             case DISCONNECTING_TIMEOUT:
337                 return "DISCONNECTING_TIMEOUT";
338             default:
339                 return "UNKNOWN(" + what + ")";
340         }
341     }
342 
343     @VisibleForTesting
addQueuedAction(int action)344     void addQueuedAction(int action) {
345         addQueuedAction(action, 0);
346     }
347 
addQueuedAction(int action, Object data)348     private void addQueuedAction(int action, Object data) {
349         mQueuedActions.add(new Pair<Integer, Object>(action, data));
350     }
351 
addQueuedAction(int action, int data)352     private void addQueuedAction(int action, int data) {
353         mQueuedActions.add(new Pair<Integer, Object>(action, data));
354     }
355 
356     @VisibleForTesting
getCall(int... states)357     HfpClientCall getCall(int... states) {
358         debug("getFromCallsWithStates states:" + Arrays.toString(states));
359         for (HfpClientCall c : mCalls.values()) {
360             for (int s : states) {
361                 if (c.getState() == s) {
362                     return c;
363                 }
364             }
365         }
366         return null;
367     }
368 
369     @VisibleForTesting
callsInState(int state)370     int callsInState(int state) {
371         int i = 0;
372         for (HfpClientCall c : mCalls.values()) {
373             if (c.getState() == state) {
374                 i++;
375             }
376         }
377 
378         return i;
379     }
380 
sendCallChangedIntent(HfpClientCall c)381     private void sendCallChangedIntent(HfpClientCall c) {
382         debug("sendCallChangedIntent " + c);
383         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
384         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
385 
386         if (mService.getPackageManager().hasSystemFeature(FEATURE_WATCH)) {
387             debug("Send legacy call");
388             intent.putExtra(
389                     BluetoothHeadsetClient.EXTRA_CALL, HeadsetClientService.toLegacyCall(c));
390         } else {
391             intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c);
392         }
393 
394         mService.sendBroadcast(
395                 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
396         HfpClientConnectionService.onCallChanged(c.getDevice(), c);
397     }
398 
sendNetworkStateChangedIntent(BluetoothDevice device)399     private void sendNetworkStateChangedIntent(BluetoothDevice device) {
400         if (device == null) {
401             return;
402         }
403         NetworkServiceState networkServiceState =
404                 new NetworkServiceState(
405                         device,
406                         mIndicatorNetworkState == HeadsetClientHalConstants.NETWORK_STATE_AVAILABLE,
407                         mOperatorName,
408                         mIndicatorNetworkSignal,
409                         mIndicatorNetworkType == HeadsetClientHalConstants.SERVICE_TYPE_ROAMING);
410 
411         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_NETWORK_SERVICE_STATE_CHANGED);
412         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
413         intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SERVICE_STATE, networkServiceState);
414 
415         mService.sendBroadcastMultiplePermissions(
416                 intent,
417                 new String[] {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
418                 Utils.getTempBroadcastOptions());
419     }
420 
queryCallsStart()421     private boolean queryCallsStart() {
422         debug("queryCallsStart");
423         mNativeInterface.queryCurrentCalls(mCurrentDevice);
424         addQueuedAction(QUERY_CURRENT_CALLS, 0);
425         return true;
426     }
427 
queryCallsDone()428     private void queryCallsDone() {
429         debug("queryCallsDone");
430         // mCalls has two types of calls:
431         // (a) Calls that are received from AG of a previous iteration of queryCallsStart()
432         // (b) Calls that are outgoing initiated from HF
433         // mCallsUpdate has all calls received from queryCallsUpdate() in current iteration of
434         // queryCallsStart().
435         //
436         // We use the following steps to make sure that calls are update correctly.
437         //
438         // If there are no calls initiated from HF (i.e. ID = -1) then:
439         // 1. All IDs which are common in mCalls & mCallsUpdate are updated and the upper layers are
440         // informed of the change calls (if any changes).
441         // 2. All IDs that are in mCalls but *not* in mCallsUpdate will be removed from mCalls and
442         // the calls should be terminated
443         // 3. All IDs that are new in mCallsUpdated should be added as new calls to mCalls.
444         //
445         // If there is an outgoing HF call, it is important to associate that call with one of the
446         // mCallsUpdated calls hence,
447         // 1. If from the above procedure we get N extra calls (i.e. {3}):
448         // choose the first call as the one to associate with the HF call.
449 
450         // Create set of IDs for added calls, removed calls and consistent calls.
451         // WARN!!! Java Map -> Set has association hence changes to Set are reflected in the Map
452         // itself (i.e. removing an element from Set removes it from the Map hence use copy).
453         Set<Integer> currCallIdSet = new HashSet<Integer>();
454         currCallIdSet.addAll(mCalls.keySet());
455         // Remove the entry for unassigned call.
456         currCallIdSet.remove(HF_ORIGINATED_CALL_ID);
457 
458         Set<Integer> newCallIdSet = new HashSet<Integer>();
459         newCallIdSet.addAll(mCallsUpdate.keySet());
460 
461         // Added.
462         Set<Integer> callAddedIds = new HashSet<Integer>();
463         callAddedIds.addAll(newCallIdSet);
464         callAddedIds.removeAll(currCallIdSet);
465 
466         // Removed.
467         Set<Integer> callRemovedIds = new HashSet<Integer>();
468         callRemovedIds.addAll(currCallIdSet);
469         callRemovedIds.removeAll(newCallIdSet);
470 
471         // Retained.
472         Set<Integer> callRetainedIds = new HashSet<Integer>();
473         callRetainedIds.addAll(currCallIdSet);
474         callRetainedIds.retainAll(newCallIdSet);
475 
476         debug(
477                 "currCallIdSet "
478                         + mCalls.keySet()
479                         + " newCallIdSet "
480                         + newCallIdSet
481                         + " callAddedIds "
482                         + callAddedIds
483                         + " callRemovedIds "
484                         + callRemovedIds
485                         + " callRetainedIds "
486                         + callRetainedIds);
487 
488         // First thing is to try to associate the outgoing HF with a valid call.
489         Integer hfOriginatedAssoc = -1;
490         if (mCalls.containsKey(HF_ORIGINATED_CALL_ID)) {
491             HfpClientCall c = mCalls.get(HF_ORIGINATED_CALL_ID);
492             long cCreationElapsed = c.getCreationElapsedMilli();
493             if (callAddedIds.size() > 0) {
494                 debug("Associating the first call with HF originated call");
495                 hfOriginatedAssoc = (Integer) callAddedIds.toArray()[0];
496                 mCalls.put(hfOriginatedAssoc, mCalls.get(HF_ORIGINATED_CALL_ID));
497                 mCalls.remove(HF_ORIGINATED_CALL_ID);
498 
499                 // Adjust this call in above sets.
500                 callAddedIds.remove(hfOriginatedAssoc);
501                 callRetainedIds.add(hfOriginatedAssoc);
502             } else if (SystemClock.elapsedRealtime() - cCreationElapsed > OUTGOING_TIMEOUT_MILLI) {
503                 warn("Outgoing call did not see a response, clear the calls and send CHUP");
504                 // We send a terminate because we are in a bad state and trying to
505                 // recover.
506                 terminateCall();
507 
508                 // Clean out the state for outgoing call.
509                 for (Integer idx : mCalls.keySet()) {
510                     HfpClientCall c1 = mCalls.get(idx);
511                     c1.setState(HfpClientCall.CALL_STATE_TERMINATED);
512                     sendCallChangedIntent(c1);
513                 }
514                 mCalls.clear();
515 
516                 // We return here, if there's any update to the phone we should get a
517                 // follow up by getting some call indicators and hence update the calls.
518                 return;
519             }
520         }
521 
522         debug(
523                 "ADJUST: currCallIdSet "
524                         + mCalls.keySet()
525                         + " newCallIdSet "
526                         + newCallIdSet
527                         + " callAddedIds "
528                         + callAddedIds
529                         + " callRemovedIds "
530                         + callRemovedIds
531                         + " callRetainedIds "
532                         + callRetainedIds);
533 
534         // Terminate & remove the calls that are done.
535         for (Integer idx : callRemovedIds) {
536             HfpClientCall c = mCalls.remove(idx);
537             c.setState(HfpClientCall.CALL_STATE_TERMINATED);
538             sendCallChangedIntent(c);
539         }
540 
541         // Add the new calls.
542         for (Integer idx : callAddedIds) {
543             HfpClientCall c = mCallsUpdate.get(idx);
544             mCalls.put(idx, c);
545             sendCallChangedIntent(c);
546         }
547 
548         // Update the existing calls.
549         for (Integer idx : callRetainedIds) {
550             HfpClientCall cOrig = mCalls.get(idx);
551             HfpClientCall cUpdate = mCallsUpdate.get(idx);
552 
553             // If any of the fields differs, update and send intent
554             if (!cOrig.getNumber().equals(cUpdate.getNumber())
555                     || cOrig.getState() != cUpdate.getState()
556                     || cOrig.isMultiParty() != cUpdate.isMultiParty()) {
557 
558                 // Update the necessary fields.
559                 cOrig.setNumber(cUpdate.getNumber());
560                 cOrig.setState(cUpdate.getState());
561                 cOrig.setMultiParty(cUpdate.isMultiParty());
562 
563                 // Send update with original object (UUID, idx).
564                 sendCallChangedIntent(cOrig);
565             }
566         }
567 
568         if (mCalls.size() > 0) {
569             // Continue polling even if not enabled until the new outgoing call is associated with
570             // a valid call on the phone. The polling would at most continue until
571             // OUTGOING_TIMEOUT_MILLI. This handles the potential scenario where the phone creates
572             // and terminates a call before the first QUERY_CURRENT_CALLS completes.
573             if (mClccPollDuringCall || (mCalls.containsKey(HF_ORIGINATED_CALL_ID))) {
574                 sendMessageDelayed(
575                         QUERY_CURRENT_CALLS,
576                         mService.getResources()
577                                 .getInteger(R.integer.hfp_clcc_poll_interval_during_call));
578             } else {
579                 if (getCall(HfpClientCall.CALL_STATE_INCOMING) != null) {
580                     debug("Still have incoming call; polling");
581                     sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS);
582                 } else {
583                     removeMessages(QUERY_CURRENT_CALLS);
584                 }
585             }
586         }
587 
588         mCallsUpdate.clear();
589     }
590 
queryCallsUpdate( int id, int state, String number, boolean multiParty, boolean outgoing)591     private void queryCallsUpdate(
592             int id, int state, String number, boolean multiParty, boolean outgoing) {
593         debug("queryCallsUpdate: " + id);
594         mCallsUpdate.put(
595                 id,
596                 new HfpClientCall(
597                         mCurrentDevice, id, state, number, multiParty, outgoing, mInBandRing));
598     }
599 
acceptCall(int flag)600     private void acceptCall(int flag) {
601         int action = -1;
602 
603         debug("acceptCall: (" + flag + ")");
604 
605         HfpClientCall c =
606                 getCall(HfpClientCall.CALL_STATE_INCOMING, HfpClientCall.CALL_STATE_WAITING);
607         if (c == null) {
608             c =
609                     getCall(
610                             HfpClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD,
611                             HfpClientCall.CALL_STATE_HELD);
612 
613             if (c == null) {
614                 return;
615             }
616         }
617 
618         debug("Call to accept: " + c);
619         switch (c.getState()) {
620             case HfpClientCall.CALL_STATE_INCOMING:
621                 if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
622                     return;
623                 }
624                 action = HeadsetClientHalConstants.CALL_ACTION_ATA;
625                 break;
626             case HfpClientCall.CALL_STATE_WAITING:
627                 if (callsInState(HfpClientCall.CALL_STATE_ACTIVE) == 0) {
628                     // if no active calls present only plain accept is allowed
629                     if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
630                         return;
631                     }
632                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
633                     break;
634                 }
635 
636                 // if active calls are present then we have the option to either terminate the
637                 // existing call or hold the existing call. We hold the other call by default.
638                 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD
639                         || flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
640                     debug("Accepting call with accept and hold");
641                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
642                 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) {
643                     debug("Accepting call with accept and reject");
644                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1;
645                 } else {
646                     error("Accept call with invalid flag: " + flag);
647                     return;
648                 }
649                 break;
650             case HfpClientCall.CALL_STATE_HELD:
651                 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) {
652                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
653                 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) {
654                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1;
655                 } else if (getCall(HfpClientCall.CALL_STATE_ACTIVE) != null) {
656                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_3;
657                 } else {
658                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
659                 }
660                 break;
661             case HfpClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
662                 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_1;
663                 break;
664             case HfpClientCall.CALL_STATE_ALERTING:
665             case HfpClientCall.CALL_STATE_ACTIVE:
666             case HfpClientCall.CALL_STATE_DIALING:
667             default:
668                 return;
669         }
670 
671         if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) {
672             // When unholding a call over Bluetooth make sure to route audio.
673             routeHfpAudio(true);
674         }
675 
676         if (mNativeInterface.handleCallAction(mCurrentDevice, action, 0)) {
677             addQueuedAction(ACCEPT_CALL, action);
678         } else {
679             error("ERROR: Couldn't accept a call, action:" + action);
680         }
681     }
682 
rejectCall()683     private void rejectCall() {
684         int action;
685 
686         debug("rejectCall");
687 
688         HfpClientCall c =
689                 getCall(
690                         HfpClientCall.CALL_STATE_INCOMING,
691                         HfpClientCall.CALL_STATE_WAITING,
692                         HfpClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD,
693                         HfpClientCall.CALL_STATE_HELD);
694         if (c == null) {
695             debug("No call to reject, returning.");
696             return;
697         }
698 
699         switch (c.getState()) {
700             case HfpClientCall.CALL_STATE_INCOMING:
701                 action = HeadsetClientHalConstants.CALL_ACTION_CHUP;
702                 break;
703             case HfpClientCall.CALL_STATE_WAITING:
704             case HfpClientCall.CALL_STATE_HELD:
705                 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_0;
706                 break;
707             case HfpClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
708                 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_2;
709                 break;
710             case HfpClientCall.CALL_STATE_ACTIVE:
711             case HfpClientCall.CALL_STATE_DIALING:
712             case HfpClientCall.CALL_STATE_ALERTING:
713             default:
714                 return;
715         }
716 
717         if (mNativeInterface.handleCallAction(mCurrentDevice, action, 0)) {
718             debug("Reject call action " + action);
719             addQueuedAction(REJECT_CALL, action);
720         } else {
721             error("ERROR: Couldn't reject a call, action:" + action);
722         }
723     }
724 
holdCall()725     private void holdCall() {
726         int action;
727 
728         debug("holdCall");
729 
730         HfpClientCall c = getCall(HfpClientCall.CALL_STATE_INCOMING);
731         if (c != null) {
732             action = HeadsetClientHalConstants.CALL_ACTION_BTRH_0;
733         } else {
734             c = getCall(HfpClientCall.CALL_STATE_ACTIVE);
735             if (c == null) {
736                 return;
737             }
738 
739             action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
740         }
741 
742         if (mNativeInterface.handleCallAction(mCurrentDevice, action, 0)) {
743             addQueuedAction(HOLD_CALL, action);
744         } else {
745             error("ERROR: Couldn't hold a call, action:" + action);
746         }
747     }
748 
terminateCall()749     private void terminateCall() {
750         debug("terminateCall");
751 
752         int action = HeadsetClientHalConstants.CALL_ACTION_CHUP;
753 
754         HfpClientCall c =
755                 getCall(
756                         HfpClientCall.CALL_STATE_DIALING,
757                         HfpClientCall.CALL_STATE_ALERTING,
758                         HfpClientCall.CALL_STATE_ACTIVE);
759         if (c == null) {
760             // If the call being terminated is currently held, switch the action to CHLD_0
761             c = getCall(HfpClientCall.CALL_STATE_HELD);
762             action = HeadsetClientHalConstants.CALL_ACTION_CHLD_0;
763         }
764         if (c != null) {
765             if (mNativeInterface.handleCallAction(mCurrentDevice, action, 0)) {
766                 addQueuedAction(TERMINATE_CALL, action);
767             } else {
768                 error("ERROR: Couldn't terminate outgoing call");
769             }
770         }
771     }
772 
773     @VisibleForTesting
enterPrivateMode(int idx)774     void enterPrivateMode(int idx) {
775         debug("enterPrivateMode: " + idx);
776 
777         HfpClientCall c = mCalls.get(idx);
778 
779         if (c == null || c.getState() != HfpClientCall.CALL_STATE_ACTIVE || !c.isMultiParty()) {
780             return;
781         }
782 
783         if (mNativeInterface.handleCallAction(
784                 mCurrentDevice, HeadsetClientHalConstants.CALL_ACTION_CHLD_2X, idx)) {
785             addQueuedAction(ENTER_PRIVATE_MODE, c);
786         } else {
787             error("ERROR: Couldn't enter private " + " id:" + idx);
788         }
789     }
790 
791     @VisibleForTesting
explicitCallTransfer()792     void explicitCallTransfer() {
793         debug("explicitCallTransfer");
794 
795         // can't transfer call if there is not enough call parties
796         if (mCalls.size() < 2) {
797             return;
798         }
799 
800         if (mNativeInterface.handleCallAction(
801                 mCurrentDevice, HeadsetClientHalConstants.CALL_ACTION_CHLD_4, -1)) {
802             addQueuedAction(EXPLICIT_CALL_TRANSFER);
803         } else {
804             error("ERROR: Couldn't transfer call");
805         }
806     }
807 
getCurrentAgFeaturesBundle()808     public Bundle getCurrentAgFeaturesBundle() {
809         Bundle b = new Bundle();
810         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY)
811                 == HeadsetClientHalConstants.PEER_FEAT_3WAY) {
812             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true);
813         }
814         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC)
815                 == HeadsetClientHalConstants.PEER_FEAT_VREC) {
816             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true);
817         }
818         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT)
819                 == HeadsetClientHalConstants.PEER_FEAT_REJECT) {
820             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true);
821         }
822         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC)
823                 == HeadsetClientHalConstants.PEER_FEAT_ECC) {
824             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true);
825         }
826 
827         // add individual CHLD support extras
828         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)
829                 == HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) {
830             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, true);
831         }
832         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL)
833                 == HeadsetClientHalConstants.CHLD_FEAT_REL) {
834             b.putBoolean(
835                     BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true);
836         }
837         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)
838                 == HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) {
839             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true);
840         }
841         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE)
842                 == HeadsetClientHalConstants.CHLD_FEAT_MERGE) {
843             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true);
844         }
845         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)
846                 == HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) {
847             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true);
848         }
849 
850         return b;
851     }
852 
getCurrentAgFeatures()853     public Set<Integer> getCurrentAgFeatures() {
854         HashSet<Integer> features = new HashSet<>();
855 
856         if (isSupported(mPeerFeatures, HeadsetClientHalConstants.PEER_FEAT_3WAY)) {
857             features.add(HeadsetClientHalConstants.PEER_FEAT_3WAY);
858         }
859         if (isSupported(mPeerFeatures, HeadsetClientHalConstants.PEER_FEAT_VREC)) {
860             features.add(HeadsetClientHalConstants.PEER_FEAT_VREC);
861         }
862         if (isSupported(mPeerFeatures, HeadsetClientHalConstants.PEER_FEAT_REJECT)) {
863             features.add(HeadsetClientHalConstants.PEER_FEAT_REJECT);
864         }
865         if (isSupported(mPeerFeatures, HeadsetClientHalConstants.PEER_FEAT_ECC)) {
866             features.add(HeadsetClientHalConstants.PEER_FEAT_ECC);
867         }
868 
869         // add individual CHLD support extras
870         if (isSupported(mChldFeatures, HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)) {
871             features.add(HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC);
872         }
873         if (isSupported(mChldFeatures, HeadsetClientHalConstants.CHLD_FEAT_REL)) {
874             features.add(HeadsetClientHalConstants.CHLD_FEAT_REL);
875         }
876         if (isSupported(mChldFeatures, HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)) {
877             features.add(HeadsetClientHalConstants.CHLD_FEAT_REL_ACC);
878         }
879         if (isSupported(mChldFeatures, HeadsetClientHalConstants.CHLD_FEAT_MERGE)) {
880             features.add(HeadsetClientHalConstants.CHLD_FEAT_MERGE);
881         }
882         if (isSupported(mChldFeatures, HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)) {
883             features.add(HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH);
884         }
885 
886         return features;
887     }
888 
isSupported(int bitfield, int mask)889     private static boolean isSupported(int bitfield, int mask) {
890         return (bitfield & mask) == mask;
891     }
892 
HeadsetClientStateMachine( AdapterService adapterService, HeadsetClientService context, HeadsetService headsetService, Looper looper, NativeInterface nativeInterface)893     HeadsetClientStateMachine(
894             AdapterService adapterService,
895             HeadsetClientService context,
896             HeadsetService headsetService,
897             Looper looper,
898             NativeInterface nativeInterface) {
899         super(TAG, looper);
900         mAdapterService = requireNonNull(adapterService);
901         mService = requireNonNull(context);
902         mNativeInterface = nativeInterface;
903         mAudioManager = mService.getAudioManager();
904         mHeadsetService = headsetService;
905 
906         mVendorProcessor = new VendorCommandResponseProcessor(mService, mNativeInterface);
907 
908         mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
909         mAudioWbs = false;
910         mAudioSWB = false;
911         mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED;
912 
913         mAudioRouteAllowed =
914                 context.getResources()
915                         .getBoolean(R.bool.headset_client_initial_audio_route_allowed);
916 
917         mAudioRouteAllowed =
918                 SystemProperties.getBoolean(
919                         "bluetooth.headset_client.initial_audio_route.enabled", mAudioRouteAllowed);
920 
921         mClccPollDuringCall =
922                 SystemProperties.getBoolean(
923                         "bluetooth.hfp.clcc_poll_during_call.enabled",
924                         mService.getResources().getBoolean(R.bool.hfp_clcc_poll_during_call));
925 
926         mHsClientAudioPolicy = new BluetoothSinkAudioPolicy.Builder().build();
927         mConnectingTimePolicyProperty =
928                 getAudioPolicySystemProp(
929                         "bluetooth.headset_client.audio_policy.connecting_time.config");
930         mInBandRingtonePolicyProperty =
931                 getAudioPolicySystemProp(
932                         "bluetooth.headset_client.audio_policy.in_band_ringtone.config");
933         mForceSetAudioPolicyProperty =
934                 SystemProperties.getBoolean(
935                         "bluetooth.headset_client.audio_policy.force_enabled", false);
936 
937         mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE;
938         mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME;
939         mIndicatorNetworkSignal = 0;
940         mIndicatorBatteryLevel = 0;
941 
942         mOperatorName = null;
943         mSubscriberInfo = null;
944 
945         mQueuedActions = new ArrayDeque<>();
946 
947         mCalls.clear();
948         mCallsUpdate.clear();
949 
950         mDisconnected = new Disconnected();
951         mConnecting = new Connecting();
952         mConnected = new Connected();
953         mAudioOn = new AudioOn();
954         mDisconnecting = new Disconnecting();
955 
956         addState(mDisconnected);
957         addState(mConnecting);
958         addState(mConnected);
959         addState(mAudioOn, mConnected);
960         if (Flags.hfpClientDisconnectingState()) {
961             addState(mDisconnecting);
962         }
963 
964         setInitialState(mDisconnected);
965     }
966 
make( AdapterService adapterService, HeadsetClientService context, HeadsetService headsetService, Looper looper, NativeInterface nativeInterface)967     static HeadsetClientStateMachine make(
968             AdapterService adapterService,
969             HeadsetClientService context,
970             HeadsetService headsetService,
971             Looper looper,
972             NativeInterface nativeInterface) {
973         Log.d(TAG, "make");
974         HeadsetClientStateMachine hfcsm =
975                 new HeadsetClientStateMachine(
976                         adapterService, context, headsetService, looper, nativeInterface);
977         hfcsm.start();
978         return hfcsm;
979     }
980 
routeHfpAudio(boolean enable)981     synchronized void routeHfpAudio(boolean enable) {
982         debug("hfp_enable=" + enable);
983         if (enable && !sAudioIsRouted) {
984             mAudioManager.setHfpEnabled(true);
985         } else if (!enable) {
986             mAudioManager.setHfpEnabled(false);
987         }
988         sAudioIsRouted = enable;
989     }
990 
requestAudioFocus()991     private AudioFocusRequest requestAudioFocus() {
992         AudioAttributes streamAttributes =
993                 new AudioAttributes.Builder()
994                         .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
995                         .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
996                         .build();
997         AudioFocusRequest focusRequest =
998                 new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
999                         .setAudioAttributes(streamAttributes)
1000                         .build();
1001         int focusRequestStatus = mAudioManager.requestAudioFocus(focusRequest);
1002         String s =
1003                 (focusRequestStatus == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
1004                         ? "AudioFocus granted"
1005                         : "AudioFocus NOT granted";
1006         debug("AudioManager requestAudioFocus returned: " + s);
1007         return focusRequest;
1008     }
1009 
doQuit()1010     public void doQuit() {
1011         debug("doQuit");
1012         if (mCurrentDevice != null) {
1013             mNativeInterface.disconnect(mCurrentDevice);
1014         }
1015         routeHfpAudio(false);
1016         returnAudioFocusIfNecessary();
1017         quitNow();
1018     }
1019 
returnAudioFocusIfNecessary()1020     private void returnAudioFocusIfNecessary() {
1021         if (mAudioFocusRequest == null) return;
1022         mAudioManager.abandonAudioFocusRequest(mAudioFocusRequest);
1023         mAudioFocusRequest = null;
1024     }
1025 
1026     class Disconnected extends State {
1027         @Override
enter()1028         public void enter() {
1029             debug(
1030                     "Enter Disconnected: from state="
1031                             + mPrevState
1032                             + ", message="
1033                             + getMessageName(getCurrentMessage().what));
1034 
1035             // cleanup
1036             mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE;
1037             mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME;
1038             mIndicatorNetworkSignal = 0;
1039             mIndicatorBatteryLevel = 0;
1040             mInBandRing = false;
1041 
1042             mAudioWbs = false;
1043             mAudioSWB = false;
1044 
1045             // will be set on connect
1046 
1047             mOperatorName = null;
1048             mSubscriberInfo = null;
1049 
1050             mQueuedActions = new ArrayDeque<>();
1051 
1052             mCalls.clear();
1053             mCallsUpdate.clear();
1054 
1055             mPeerFeatures = 0;
1056             mChldFeatures = 0;
1057 
1058             removeMessages(QUERY_CURRENT_CALLS);
1059 
1060             if (mPrevState == mConnecting) {
1061                 broadcastConnectionState(mCurrentDevice, STATE_DISCONNECTED, STATE_CONNECTING);
1062             } else if (mPrevState == mConnected || mPrevState == mAudioOn) {
1063                 broadcastConnectionState(mCurrentDevice, STATE_DISCONNECTED, STATE_CONNECTED);
1064             } else if (Flags.hfpClientDisconnectingState()) {
1065                 if (mPrevState == mDisconnecting) {
1066                     broadcastConnectionState(
1067                             mCurrentDevice, STATE_DISCONNECTED, STATE_DISCONNECTING);
1068                 }
1069             } else if (mPrevState != null) {
1070                 // null is the default state before Disconnected
1071                 error(
1072                         "Disconnected: Illegal state transition from "
1073                                 + mPrevState.getName()
1074                                 + " to Disconnected, mCurrentDevice="
1075                                 + mCurrentDevice);
1076             }
1077             if (mHeadsetService != null && mCurrentDevice != null) {
1078                 mHeadsetService.updateInbandRinging(mCurrentDevice, false);
1079             }
1080             mCurrentDevice = null;
1081         }
1082 
1083         @Override
processMessage(Message message)1084         public synchronized boolean processMessage(Message message) {
1085             debug("Disconnected process message: " + message.what);
1086 
1087             if (mCurrentDevice != null) {
1088                 error("ERROR: current device not null in Disconnected");
1089                 return NOT_HANDLED;
1090             }
1091 
1092             switch (message.what) {
1093                 case CONNECT:
1094                     BluetoothDevice device = (BluetoothDevice) message.obj;
1095                     if (!mNativeInterface.connect(device)) {
1096                         // No state transition is involved, fire broadcast immediately
1097                         broadcastConnectionState(device, STATE_DISCONNECTED, STATE_DISCONNECTED);
1098                         break;
1099                     }
1100                     mCurrentDevice = device;
1101                     transitionTo(mConnecting);
1102                     break;
1103                 case DISCONNECT:
1104                     // ignore
1105                     break;
1106                 case StackEvent.STACK_EVENT:
1107                     StackEvent event = (StackEvent) message.obj;
1108                     debug("Stack event type: " + event.type);
1109                     switch (event.type) {
1110                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
1111                             debug(
1112                                     "Disconnected: Connection "
1113                                             + event.device
1114                                             + " state changed:"
1115                                             + event.valueInt);
1116                             processConnectionEvent(event.valueInt, event.device);
1117                             break;
1118                         default:
1119                             error("Disconnected: Unexpected stack event: " + event.type);
1120                             break;
1121                     }
1122                     break;
1123                 default:
1124                     return NOT_HANDLED;
1125             }
1126             return HANDLED;
1127         }
1128 
1129         // in Disconnected state
processConnectionEvent(int state, BluetoothDevice device)1130         private void processConnectionEvent(int state, BluetoothDevice device) {
1131             switch (state) {
1132                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED:
1133                     warn("HFPClient Connecting from Disconnected state");
1134                     if (okToConnect(device)) {
1135                         info("Incoming AG accepted");
1136                         mCurrentDevice = device;
1137                         transitionTo(mConnecting);
1138                     } else {
1139                         info(
1140                                 "Incoming AG rejected. connectionPolicy="
1141                                         + mService.getConnectionPolicy(device)
1142                                         + " bondState="
1143                                         + mAdapterService.getBondState(device));
1144                         // reject the connection and stay in Disconnected state
1145                         // itself
1146                         mNativeInterface.disconnect(device);
1147                         // the other profile connection should be initiated
1148                         // No state transition is involved, fire broadcast immediately
1149                         broadcastConnectionState(device, STATE_DISCONNECTED, STATE_DISCONNECTED);
1150                     }
1151                     break;
1152                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING:
1153                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
1154                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING:
1155                 default:
1156                     info("ignoring state: " + state);
1157                     break;
1158             }
1159         }
1160 
1161         @Override
exit()1162         public void exit() {
1163             debug("Exit Disconnected: " + getMessageName(getCurrentMessage().what));
1164             mPrevState = this;
1165         }
1166     }
1167 
1168     class Connecting extends State {
1169         @Override
enter()1170         public void enter() {
1171             debug("Enter Connecting: " + getMessageName(getCurrentMessage().what));
1172             // This message is either consumed in processMessage or
1173             // removed in exit. It is safe to send a CONNECTING_TIMEOUT here since
1174             // the only transition is when connection attempt is initiated.
1175             sendMessageDelayed(CONNECTING_TIMEOUT, CONNECTING_TIMEOUT_MS);
1176             if (mPrevState == mDisconnected) {
1177                 broadcastConnectionState(mCurrentDevice, STATE_CONNECTING, STATE_DISCONNECTED);
1178             } else {
1179                 String prevStateName = mPrevState == null ? "null" : mPrevState.getName();
1180                 error(
1181                         "Connecting: Illegal state transition from "
1182                                 + prevStateName
1183                                 + " to Connecting");
1184             }
1185         }
1186 
1187         @Override
processMessage(Message message)1188         public synchronized boolean processMessage(Message message) {
1189             debug("Connecting process message: " + message.what);
1190 
1191             switch (message.what) {
1192                 case CONNECT:
1193                 case CONNECT_AUDIO:
1194                 case DISCONNECT:
1195                     deferMessage(message);
1196                     break;
1197                 case StackEvent.STACK_EVENT:
1198                     StackEvent event = (StackEvent) message.obj;
1199                     debug("Connecting: event type: " + event.type);
1200                     switch (event.type) {
1201                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
1202                             debug(
1203                                     "Connecting: Connection "
1204                                             + event.device
1205                                             + " state changed:"
1206                                             + event.valueInt);
1207                             processConnectionEvent(
1208                                     event.valueInt, event.valueInt2, event.valueInt3, event.device);
1209                             break;
1210                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
1211                         case StackEvent.EVENT_TYPE_NETWORK_STATE:
1212                         case StackEvent.EVENT_TYPE_ROAMING_STATE:
1213                         case StackEvent.EVENT_TYPE_NETWORK_SIGNAL:
1214                         case StackEvent.EVENT_TYPE_BATTERY_LEVEL:
1215                         case StackEvent.EVENT_TYPE_CALL:
1216                         case StackEvent.EVENT_TYPE_CALLSETUP:
1217                         case StackEvent.EVENT_TYPE_CALLHELD:
1218                         case StackEvent.EVENT_TYPE_RESP_AND_HOLD:
1219                         case StackEvent.EVENT_TYPE_CLIP:
1220                         case StackEvent.EVENT_TYPE_CALL_WAITING:
1221                         case StackEvent.EVENT_TYPE_VOLUME_CHANGED:
1222                         case StackEvent.EVENT_TYPE_IN_BAND_RINGTONE:
1223                             deferMessage(message);
1224                             break;
1225                         case StackEvent.EVENT_TYPE_CMD_RESULT:
1226                             debug(
1227                                     "Connecting: CMD_RESULT valueInt:"
1228                                             + event.valueInt
1229                                             + " mQueuedActions.size="
1230                                             + mQueuedActions.size());
1231                             if (!mQueuedActions.isEmpty()) {
1232                                 debug("queuedAction:" + mQueuedActions.peek().first);
1233                             }
1234                             Pair<Integer, Object> queuedAction = mQueuedActions.poll();
1235                             if (queuedAction == null || queuedAction.first == NO_ACTION) {
1236                                 break;
1237                             }
1238                             switch (queuedAction.first) {
1239                                 case SEND_ANDROID_AT_COMMAND:
1240                                     if (event.valueInt == StackEvent.CMD_RESULT_TYPE_OK) {
1241                                         warn("Received OK instead of +ANDROID");
1242                                     } else {
1243                                         warn("Received ERROR instead of +ANDROID");
1244                                     }
1245                                     setAudioPolicyRemoteSupported(false);
1246                                     transitionTo(mConnected);
1247                                     break;
1248                                 default:
1249                                     warn("Ignored CMD Result");
1250                                     break;
1251                             }
1252                             break;
1253 
1254                         case StackEvent.EVENT_TYPE_UNKNOWN_EVENT:
1255                             if (mVendorProcessor.isAndroidAtCommand(event.valueString)
1256                                     && processAndroidSlcCommand(event.valueString, event.device)) {
1257                                 transitionTo(mConnected);
1258                             } else {
1259                                 error(
1260                                         "Unknown event :"
1261                                                 + event.valueString
1262                                                 + " for device "
1263                                                 + event.device);
1264                             }
1265                             break;
1266 
1267                         case StackEvent.EVENT_TYPE_SUBSCRIBER_INFO:
1268                         case StackEvent.EVENT_TYPE_CURRENT_CALLS:
1269                         case StackEvent.EVENT_TYPE_OPERATOR_NAME:
1270                         default:
1271                             error("Connecting: ignoring stack event: " + event.type);
1272                             break;
1273                     }
1274                     break;
1275                 case CONNECTING_TIMEOUT:
1276                     // We timed out trying to connect, transition to disconnected.
1277                     warn("Connection timeout for " + mCurrentDevice);
1278                     transitionTo(mDisconnected);
1279                     break;
1280 
1281                 default:
1282                     warn("Message not handled " + message);
1283                     return NOT_HANDLED;
1284             }
1285             return HANDLED;
1286         }
1287 
1288         // in Connecting state
processConnectionEvent( int state, int peerFeat, int chldFeat, BluetoothDevice device)1289         private void processConnectionEvent(
1290                 int state, int peerFeat, int chldFeat, BluetoothDevice device) {
1291             switch (state) {
1292                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
1293                     transitionTo(mDisconnected);
1294                     break;
1295 
1296                 case HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1297                     debug("HFPClient Connected from Connecting state");
1298 
1299                     mPeerFeatures = peerFeat;
1300                     mChldFeatures = chldFeat;
1301 
1302                     // We do not support devices which do not support enhanced call status (ECS).
1303                     if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECS) == 0) {
1304                         mNativeInterface.disconnect(device);
1305                         return;
1306                     }
1307 
1308                     // Send AT+NREC to remote if supported by audio
1309                     if (HeadsetClientHalConstants.HANDSFREECLIENT_NREC_SUPPORTED
1310                             && ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECNR)
1311                                     == HeadsetClientHalConstants.PEER_FEAT_ECNR)) {
1312                         if (mNativeInterface.sendATCmd(
1313                                 mCurrentDevice,
1314                                 HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_NREC,
1315                                 1,
1316                                 0,
1317                                 null)) {
1318                             addQueuedAction(DISABLE_NREC);
1319                         } else {
1320                             error("Failed to send NREC");
1321                         }
1322                     }
1323 
1324                     int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
1325                     deferMessage(
1326                             obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0));
1327                     // Mic is either in ON state (full volume) or OFF state. There is no way in
1328                     // Android to change the MIC volume.
1329                     deferMessage(
1330                             obtainMessage(
1331                                     HeadsetClientStateMachine.SET_MIC_VOLUME,
1332                                     mAudioManager.isMicrophoneMute() ? 0 : 15,
1333                                     0));
1334                     // query subscriber info
1335                     deferMessage(obtainMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO));
1336 
1337                     if (!queryRemoteSupportedFeatures()) {
1338                         warn("Couldn't query Android AT remote supported!");
1339                         transitionTo(mConnected);
1340                     }
1341                     break;
1342 
1343                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED:
1344                     if (!mCurrentDevice.equals(device)) {
1345                         warn("incoming connection event, device: " + device);
1346                         // No state transition is involved, fire broadcast immediately
1347                         broadcastConnectionState(
1348                                 mCurrentDevice, STATE_DISCONNECTED, STATE_CONNECTING);
1349                         broadcastConnectionState(device, STATE_CONNECTING, STATE_DISCONNECTED);
1350 
1351                         mCurrentDevice = device;
1352                     }
1353                     break;
1354                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING:
1355                     /* outgoing connecting started */
1356                     debug("outgoing connection started, ignore");
1357                     break;
1358                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING:
1359                 default:
1360                     error("Incorrect state: " + state);
1361                     break;
1362             }
1363         }
1364 
1365         @Override
exit()1366         public void exit() {
1367             debug("Exit Connecting: " + getMessageName(getCurrentMessage().what));
1368             removeMessages(CONNECTING_TIMEOUT);
1369             mPrevState = this;
1370         }
1371     }
1372 
1373     class Connected extends State {
1374         int mCommandedSpeakerVolume = -1;
1375 
1376         @Override
enter()1377         public void enter() {
1378             debug("Enter Connected: " + getMessageName(getCurrentMessage().what));
1379             mAudioWbs = false;
1380             mAudioSWB = false;
1381             mCommandedSpeakerVolume = -1;
1382 
1383             if (mPrevState == mConnecting) {
1384                 broadcastConnectionState(mCurrentDevice, STATE_CONNECTED, STATE_CONNECTING);
1385                 if (mHeadsetService != null) {
1386                     mHeadsetService.updateInbandRinging(mCurrentDevice, true);
1387                 }
1388             } else if (mPrevState != mAudioOn) {
1389                 String prevStateName = mPrevState == null ? "null" : mPrevState.getName();
1390                 error(
1391                         "Connected: Illegal state transition from "
1392                                 + prevStateName
1393                                 + " to Connected");
1394             }
1395             mService.updateBatteryLevel();
1396         }
1397 
1398         @Override
processMessage(Message message)1399         public synchronized boolean processMessage(Message message) {
1400             debug("Connected process message: " + message.what);
1401             if (mCurrentDevice == null) {
1402                 error("ERROR: mCurrentDevice is null in Connected");
1403                 return NOT_HANDLED;
1404             }
1405 
1406             switch (message.what) {
1407                 case CONNECT:
1408                     BluetoothDevice device = (BluetoothDevice) message.obj;
1409                     if (mCurrentDevice.equals(device)) {
1410                         // already connected to this device, do nothing
1411                         break;
1412                     }
1413                     mNativeInterface.connect(device);
1414                     break;
1415                 case DISCONNECT:
1416                     BluetoothDevice dev = (BluetoothDevice) message.obj;
1417                     if (!mCurrentDevice.equals(dev)) {
1418                         break;
1419                     }
1420                     if (Flags.hfpClientDisconnectingState()) {
1421                         if (!mNativeInterface.disconnect(mCurrentDevice)) {
1422                             warn("disconnectNative failed for " + mCurrentDevice);
1423                         }
1424                         transitionTo(mDisconnecting);
1425                     } else if (!mNativeInterface.disconnect(dev)) {
1426                         error("disconnectNative failed for " + dev);
1427                     }
1428                     break;
1429 
1430                 case CONNECT_AUDIO:
1431                     if (!mNativeInterface.connectAudio(mCurrentDevice)) {
1432                         error("ERROR: Couldn't connect Audio for device");
1433                         // No state transition is involved, fire broadcast immediately
1434                         broadcastAudioState(
1435                                 mCurrentDevice,
1436                                 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
1437                                 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
1438                     } else { // We have successfully sent a connect request!
1439                         mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING;
1440                     }
1441                     break;
1442 
1443                 case DISCONNECT_AUDIO:
1444                     if (!mNativeInterface.disconnectAudio(mCurrentDevice)) {
1445                         error("ERROR: Couldn't disconnect Audio for device");
1446                     }
1447                     break;
1448 
1449                 case VOICE_RECOGNITION_START:
1450                     if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STOPPED) {
1451                         if (mNativeInterface.startVoiceRecognition(mCurrentDevice)) {
1452                             addQueuedAction(VOICE_RECOGNITION_START);
1453                         } else {
1454                             error("ERROR: Couldn't start voice recognition");
1455                         }
1456                     }
1457                     break;
1458 
1459                 case VOICE_RECOGNITION_STOP:
1460                     if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STARTED) {
1461                         if (mNativeInterface.stopVoiceRecognition(mCurrentDevice)) {
1462                             addQueuedAction(VOICE_RECOGNITION_STOP);
1463                         } else {
1464                             error("ERROR: Couldn't stop voice recognition");
1465                         }
1466                     }
1467                     break;
1468 
1469                 case SEND_VENDOR_AT_COMMAND:
1470                     {
1471                         int vendorId = message.arg1;
1472                         String atCommand = (String) (message.obj);
1473                         mVendorProcessor.sendCommand(vendorId, atCommand, mCurrentDevice);
1474                         break;
1475                     }
1476 
1477                 case SEND_BIEV:
1478                     {
1479                         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_HF_IND)
1480                                 == HeadsetClientHalConstants.PEER_FEAT_HF_IND) {
1481                             int indicatorID = message.arg1;
1482                             int value = message.arg2;
1483                             mNativeInterface.sendATCmd(
1484                                     mCurrentDevice,
1485                                     HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_BIEV,
1486                                     indicatorID,
1487                                     value,
1488                                     null);
1489                         }
1490                         break;
1491                     }
1492 
1493                     // Called only for Mute/Un-mute - Mic volume change is not allowed.
1494                 case SET_MIC_VOLUME:
1495                     break;
1496                 case SET_SPEAKER_VOLUME:
1497                     // This message should always contain the volume in AudioManager max normalized.
1498                     int amVol = message.arg1;
1499                     int hfVol = mService.amToHfVol(amVol);
1500                     if (amVol != mCommandedSpeakerVolume) {
1501                         debug("Volume" + amVol + ":" + mCommandedSpeakerVolume);
1502                         // Volume was changed by a 3rd party
1503                         mCommandedSpeakerVolume = -1;
1504                         if (mNativeInterface.setVolume(
1505                                 mCurrentDevice, HeadsetClientHalConstants.VOLUME_TYPE_SPK, hfVol)) {
1506                             addQueuedAction(SET_SPEAKER_VOLUME);
1507                         }
1508                     }
1509                     break;
1510                 case DIAL_NUMBER:
1511                     // Add the call as an outgoing call.
1512                     HfpClientCall c = (HfpClientCall) message.obj;
1513                     mCalls.put(HF_ORIGINATED_CALL_ID, c);
1514 
1515                     if (mNativeInterface.dial(mCurrentDevice, c.getNumber())) {
1516                         addQueuedAction(DIAL_NUMBER, c.getNumber());
1517                         // Start looping on calling current calls.
1518                         sendMessage(QUERY_CURRENT_CALLS);
1519                     } else {
1520                         Log.e(TAG, "ERROR: Cannot dial with a given number:" + c.toString());
1521                         // Set the call to terminated remove.
1522                         c.setState(HfpClientCall.CALL_STATE_TERMINATED);
1523                         sendCallChangedIntent(c);
1524                         mCalls.remove(HF_ORIGINATED_CALL_ID);
1525                     }
1526                     break;
1527                 case ACCEPT_CALL:
1528                     acceptCall(message.arg1);
1529                     break;
1530                 case REJECT_CALL:
1531                     rejectCall();
1532                     break;
1533                 case HOLD_CALL:
1534                     holdCall();
1535                     break;
1536                 case TERMINATE_CALL:
1537                     terminateCall();
1538                     break;
1539                 case ENTER_PRIVATE_MODE:
1540                     enterPrivateMode(message.arg1);
1541                     break;
1542                 case EXPLICIT_CALL_TRANSFER:
1543                     explicitCallTransfer();
1544                     break;
1545                 case SEND_DTMF:
1546                     if (mNativeInterface.sendDtmf(mCurrentDevice, (byte) message.arg1)) {
1547                         addQueuedAction(SEND_DTMF);
1548                     } else {
1549                         error("ERROR: Couldn't send DTMF");
1550                     }
1551                     break;
1552                 case SUBSCRIBER_INFO:
1553                     if (mNativeInterface.retrieveSubscriberInfo(mCurrentDevice)) {
1554                         addQueuedAction(SUBSCRIBER_INFO);
1555                     } else {
1556                         error("ERROR: Couldn't retrieve subscriber info");
1557                     }
1558                     break;
1559                 case QUERY_CURRENT_CALLS:
1560                     removeMessages(QUERY_CURRENT_CALLS);
1561                     debug("mClccPollDuringCall=" + mClccPollDuringCall);
1562                     // If there are ongoing calls periodically check their status.
1563                     if (mCalls.size() > 1 && mClccPollDuringCall) {
1564                         sendMessageDelayed(
1565                                 QUERY_CURRENT_CALLS,
1566                                 mService.getResources()
1567                                         .getInteger(R.integer.hfp_clcc_poll_interval_during_call));
1568                     } else if (mCalls.size() > 0) {
1569                         sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS);
1570                     }
1571                     queryCallsStart();
1572                     break;
1573                 case StackEvent.STACK_EVENT:
1574                     Intent intent = null;
1575                     StackEvent event = (StackEvent) message.obj;
1576                     debug("Connected: event type: " + event.type);
1577 
1578                     switch (event.type) {
1579                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
1580                             debug(
1581                                     "Connected: Connection state changed: "
1582                                             + event.device
1583                                             + ": "
1584                                             + event.valueInt);
1585                             processConnectionEvent(message, event.valueInt, event.device);
1586                             break;
1587                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
1588                             debug(
1589                                     "Connected: Audio state changed: "
1590                                             + event.device
1591                                             + ": "
1592                                             + event.valueInt);
1593                             processAudioEvent(event.valueInt, event.device);
1594                             break;
1595                         case StackEvent.EVENT_TYPE_NETWORK_STATE:
1596                             debug("Connected: Network state: " + event.valueInt);
1597                             mIndicatorNetworkState = event.valueInt;
1598 
1599                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1600                             intent.putExtra(
1601                                     BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, event.valueInt);
1602 
1603                             if (mIndicatorNetworkState
1604                                     == HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE) {
1605                                 mOperatorName = null;
1606                                 intent.putExtra(
1607                                         BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, mOperatorName);
1608                             }
1609 
1610                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1611                             mService.sendBroadcast(
1612                                     intent,
1613                                     BLUETOOTH_CONNECT,
1614                                     Utils.getTempBroadcastOptions().toBundle());
1615                             sendNetworkStateChangedIntent(event.device);
1616 
1617                             if (mIndicatorNetworkState
1618                                     == HeadsetClientHalConstants.NETWORK_STATE_AVAILABLE) {
1619                                 if (mNativeInterface.queryCurrentOperatorName(mCurrentDevice)) {
1620                                     addQueuedAction(QUERY_OPERATOR_NAME);
1621                                 } else {
1622                                     error("ERROR: Couldn't query operator name");
1623                                 }
1624                             }
1625                             break;
1626                         case StackEvent.EVENT_TYPE_ROAMING_STATE:
1627                             mIndicatorNetworkType = event.valueInt;
1628 
1629                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1630                             intent.putExtra(
1631                                     BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, event.valueInt);
1632                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1633                             mService.sendBroadcast(
1634                                     intent,
1635                                     BLUETOOTH_CONNECT,
1636                                     Utils.getTempBroadcastOptions().toBundle());
1637                             sendNetworkStateChangedIntent(event.device);
1638                             break;
1639                         case StackEvent.EVENT_TYPE_NETWORK_SIGNAL:
1640                             mIndicatorNetworkSignal = event.valueInt;
1641 
1642                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1643                             intent.putExtra(
1644                                     BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH,
1645                                     event.valueInt);
1646                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1647                             mService.sendBroadcast(
1648                                     intent,
1649                                     BLUETOOTH_CONNECT,
1650                                     Utils.getTempBroadcastOptions().toBundle());
1651                             sendNetworkStateChangedIntent(event.device);
1652                             break;
1653                         case StackEvent.EVENT_TYPE_BATTERY_LEVEL:
1654                             mIndicatorBatteryLevel = event.valueInt;
1655                             mService.handleBatteryLevelChanged(event.device, event.valueInt);
1656 
1657                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1658                             intent.putExtra(
1659                                     BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, event.valueInt);
1660                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1661                             mService.sendBroadcast(
1662                                     intent,
1663                                     BLUETOOTH_CONNECT,
1664                                     Utils.getTempBroadcastOptions().toBundle());
1665                             break;
1666                         case StackEvent.EVENT_TYPE_OPERATOR_NAME:
1667                             mOperatorName = event.valueString;
1668 
1669                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1670                             intent.putExtra(
1671                                     BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, event.valueString);
1672                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1673                             mService.sendBroadcast(
1674                                     intent,
1675                                     BLUETOOTH_CONNECT,
1676                                     Utils.getTempBroadcastOptions().toBundle());
1677                             sendNetworkStateChangedIntent(event.device);
1678                             break;
1679                         case StackEvent.EVENT_TYPE_VR_STATE_CHANGED:
1680                             int oldState = mVoiceRecognitionActive;
1681                             mVoiceRecognitionActive = event.valueInt;
1682                             broadcastVoiceRecognitionStateChanged(
1683                                     event.device, oldState, mVoiceRecognitionActive);
1684                             break;
1685                         case StackEvent.EVENT_TYPE_CALL:
1686                         case StackEvent.EVENT_TYPE_CALLSETUP:
1687                         case StackEvent.EVENT_TYPE_CALLHELD:
1688                         case StackEvent.EVENT_TYPE_RESP_AND_HOLD:
1689                         case StackEvent.EVENT_TYPE_CLIP:
1690                         case StackEvent.EVENT_TYPE_CALL_WAITING:
1691                             sendMessage(QUERY_CURRENT_CALLS);
1692                             break;
1693                         case StackEvent.EVENT_TYPE_CURRENT_CALLS:
1694                             queryCallsUpdate(
1695                                     event.valueInt,
1696                                     event.valueInt3,
1697                                     event.valueString,
1698                                     event.valueInt4
1699                                             == HeadsetClientHalConstants.CALL_MPTY_TYPE_MULTI,
1700                                     event.valueInt2
1701                                             == HeadsetClientHalConstants.CALL_DIRECTION_OUTGOING);
1702                             break;
1703                         case StackEvent.EVENT_TYPE_VOLUME_CHANGED:
1704                             if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) {
1705                                 mCommandedSpeakerVolume = mService.hfToAmVol(event.valueInt2);
1706                                 debug("AM volume set to " + mCommandedSpeakerVolume);
1707                                 boolean show_volume =
1708                                         SystemProperties.getBoolean(
1709                                                 "bluetooth.hfp_volume_control.enabled", true);
1710                                 mAudioManager.setStreamVolume(
1711                                         AudioManager.STREAM_VOICE_CALL,
1712                                         +mCommandedSpeakerVolume,
1713                                         show_volume ? AudioManager.FLAG_SHOW_UI : 0);
1714                             } else if (event.valueInt
1715                                     == HeadsetClientHalConstants.VOLUME_TYPE_MIC) {
1716                                 mAudioManager.setMicrophoneMute(event.valueInt2 == 0);
1717                             }
1718                             break;
1719                         case StackEvent.EVENT_TYPE_CMD_RESULT:
1720                             Pair<Integer, Object> queuedAction = mQueuedActions.poll();
1721 
1722                             // should not happen but...
1723                             if (queuedAction == null || queuedAction.first == NO_ACTION) {
1724                                 break;
1725                             }
1726 
1727                             debug(
1728                                     "Connected: command result: "
1729                                             + event.valueInt
1730                                             + " queuedAction: "
1731                                             + queuedAction.first);
1732 
1733                             switch (queuedAction.first) {
1734                                 case QUERY_CURRENT_CALLS:
1735                                     queryCallsDone();
1736                                     break;
1737                                 case VOICE_RECOGNITION_START:
1738                                     if (event.valueInt == AT_OK) {
1739                                         oldState = mVoiceRecognitionActive;
1740                                         mVoiceRecognitionActive =
1741                                                 HeadsetClientHalConstants.VR_STATE_STARTED;
1742                                         broadcastVoiceRecognitionStateChanged(
1743                                                 event.device, oldState, mVoiceRecognitionActive);
1744                                     }
1745                                     break;
1746                                 case VOICE_RECOGNITION_STOP:
1747                                     if (event.valueInt == AT_OK) {
1748                                         oldState = mVoiceRecognitionActive;
1749                                         mVoiceRecognitionActive =
1750                                                 HeadsetClientHalConstants.VR_STATE_STOPPED;
1751                                         broadcastVoiceRecognitionStateChanged(
1752                                                 event.device, oldState, mVoiceRecognitionActive);
1753                                     }
1754                                     break;
1755                                 case SEND_ANDROID_AT_COMMAND:
1756                                     debug("Connected: Received OK for AT+ANDROID");
1757                                     break;
1758                                 default:
1759                                     warn("Unhandled AT OK " + event);
1760                                     break;
1761                             }
1762 
1763                             break;
1764                         case StackEvent.EVENT_TYPE_SUBSCRIBER_INFO:
1765                             mSubscriberInfo = event.valueString;
1766                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1767                             intent.putExtra(
1768                                     BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, mSubscriberInfo);
1769                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1770                             mService.sendBroadcast(
1771                                     intent,
1772                                     BLUETOOTH_CONNECT,
1773                                     Utils.getTempBroadcastOptions().toBundle());
1774                             break;
1775                         case StackEvent.EVENT_TYPE_IN_BAND_RINGTONE:
1776                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1777                             mInBandRing = event.valueInt == IN_BAND_RING_ENABLED;
1778                             intent.putExtra(
1779                                     BluetoothHeadsetClient.EXTRA_IN_BAND_RING, event.valueInt);
1780                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1781                             mService.sendBroadcast(
1782                                     intent,
1783                                     BLUETOOTH_CONNECT,
1784                                     Utils.getTempBroadcastOptions().toBundle());
1785                             debug(event.device.toString() + "onInBandRing" + event.valueInt);
1786                             break;
1787                         case StackEvent.EVENT_TYPE_RING_INDICATION:
1788                             // Ringing is not handled at this indication and rather should be
1789                             // implemented (by the client of this service). Use the
1790                             // CALL_STATE_INCOMING (and similar) handle ringing.
1791                             break;
1792                         case StackEvent.EVENT_TYPE_UNKNOWN_EVENT:
1793                             if (!mVendorProcessor.processEvent(event.valueString, event.device)) {
1794                                 error(
1795                                         "Unknown event :"
1796                                                 + event.valueString
1797                                                 + " for device "
1798                                                 + event.device);
1799                             }
1800                             break;
1801                         default:
1802                             error("Unknown stack event: " + event.type);
1803                             break;
1804                     }
1805 
1806                     break;
1807                 default:
1808                     return NOT_HANDLED;
1809             }
1810             return HANDLED;
1811         }
1812 
broadcastVoiceRecognitionStateChanged( BluetoothDevice device, int oldState, int newState)1813         private void broadcastVoiceRecognitionStateChanged(
1814                 BluetoothDevice device, int oldState, int newState) {
1815             if (oldState == newState) {
1816                 return;
1817             }
1818             Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1819             intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, newState);
1820             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1821             mService.sendBroadcast(
1822                     intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
1823         }
1824 
1825         // in Connected state
processConnectionEvent(Message message, int state, BluetoothDevice device)1826         private void processConnectionEvent(Message message, int state, BluetoothDevice device) {
1827             switch (state) {
1828                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
1829                     debug("Connected disconnects.");
1830                     // AG disconnects
1831                     if (mCurrentDevice.equals(device)) {
1832                         if (Flags.hfpClientDisconnectingState()) {
1833                             transitionTo(mDisconnecting);
1834                             // message is deferred to be processed in the disconnecting state
1835                             deferMessage(message);
1836                         } else {
1837                             transitionTo(mDisconnected);
1838                         }
1839                     } else {
1840                         error("Disconnected from unknown device: " + device);
1841                     }
1842                     break;
1843                 default:
1844                     error("Connection State Device: " + device + " bad state: " + state);
1845                     break;
1846             }
1847         }
1848 
1849         // in Connected state
processAudioEvent(int state, BluetoothDevice device)1850         private void processAudioEvent(int state, BluetoothDevice device) {
1851             // message from old device
1852             if (!mCurrentDevice.equals(device)) {
1853                 error("Audio changed on disconnected device: " + device);
1854                 return;
1855             }
1856 
1857             switch (state) {
1858                 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED,
1859                         HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_LC3,
1860                         HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC:
1861                     mAudioSWB = state == HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_LC3;
1862                     mAudioWbs = state == HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC;
1863                     debug("mAudioRouteAllowed=" + mAudioRouteAllowed);
1864                     if (!mAudioRouteAllowed) {
1865                         info("Audio is not allowed! Disconnect SCO.");
1866                         sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
1867                         // Don't continue connecting!
1868                         return;
1869                     }
1870 
1871                     // Audio state is split in two parts, the audio focus is maintained by the
1872                     // entity exercising this service (typically the Telecom stack) and audio
1873                     // routing is handled by the bluetooth stack itself. The only reason to do so is
1874                     // because Bluetooth SCO connection from the HF role is not entirely supported
1875                     // for routing and volume purposes.
1876                     // NOTE: All calls here are routed via AudioManager methods which changes the
1877                     // routing at the Audio HAL level.
1878 
1879                     if (mService.isScoRouted()) {
1880                         StackEvent event =
1881                                 new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED);
1882                         event.valueInt = state;
1883                         event.device = device;
1884                         sendMessageDelayed(StackEvent.STACK_EVENT, event, ROUTING_DELAY_MS);
1885                         break;
1886                     }
1887 
1888                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED;
1889 
1890                     // We need to set the volume after switching into HFP mode as some Audio HALs
1891                     // reset the volume to a known-default on mode switch.
1892                     final int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
1893                     final int hfVol = mService.amToHfVol(amVol);
1894 
1895                     debug("hfp_enable=true mAudioSWB is " + mAudioSWB);
1896                     debug("hfp_enable=true mAudioWbs is " + mAudioWbs);
1897 
1898                     if (mAudioSWB) {
1899                         debug("Setting sampling rate as 32000");
1900                         mAudioManager.setHfpSamplingRate(32000);
1901                     } else if (mAudioWbs) {
1902                         debug("Setting sampling rate as 16000");
1903                         mAudioManager.setHfpSamplingRate(16000);
1904                     } else {
1905                         debug("Setting sampling rate as 8000");
1906                         mAudioManager.setHfpSamplingRate(8000);
1907                     }
1908                     debug("hf_volume " + hfVol);
1909                     routeHfpAudio(true);
1910                     mAudioFocusRequest = requestAudioFocus();
1911                     mAudioManager.setHfpVolume(hfVol);
1912                     transitionTo(mAudioOn);
1913                     break;
1914 
1915                 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTING:
1916                     // No state transition is involved, fire broadcast immediately
1917                     broadcastAudioState(
1918                             device, BluetoothHeadsetClient.STATE_AUDIO_CONNECTING, mAudioState);
1919                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING;
1920                     break;
1921 
1922                 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED:
1923                     // No state transition is involved, fire broadcast immediately
1924                     broadcastAudioState(
1925                             device, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, mAudioState);
1926                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
1927                     break;
1928 
1929                 default:
1930                     error("Audio State Device: " + device + " bad state: " + state);
1931                     break;
1932             }
1933         }
1934 
1935         @Override
exit()1936         public void exit() {
1937             debug("Exit Connected: " + getMessageName(getCurrentMessage().what));
1938             mPrevState = this;
1939         }
1940     }
1941 
1942     class Disconnecting extends State {
1943         @Override
enter()1944         public void enter() {
1945             debug(
1946                     "Disconnecting: enter disconnecting from state="
1947                             + mPrevState
1948                             + ", message="
1949                             + getMessageName(getCurrentMessage().what));
1950             if (mPrevState == mConnected || mPrevState == mAudioOn) {
1951                 broadcastConnectionState(mCurrentDevice, STATE_DISCONNECTING, STATE_CONNECTED);
1952             } else {
1953                 String prevStateName = mPrevState == null ? "null" : mPrevState.getName();
1954                 error(
1955                         "Disconnecting: Illegal state transition from "
1956                                 + prevStateName
1957                                 + " to Disconnecting");
1958             }
1959             sendMessageDelayed(DISCONNECTING_TIMEOUT, DISCONNECTING_TIMEOUT_MS);
1960         }
1961 
1962         @Override
processMessage(Message message)1963         public synchronized boolean processMessage(Message message) {
1964             debug("Disconnecting: Process message: " + message.what);
1965 
1966             switch (message.what) {
1967                     // Deferring messages as state machine objects are meant to be reused and after
1968                     // disconnect is complete we want honor other message requests
1969                 case CONNECT:
1970                 case CONNECT_AUDIO:
1971                 case DISCONNECT:
1972                 case DISCONNECT_AUDIO:
1973                     deferMessage(message);
1974                     break;
1975 
1976                 case DISCONNECTING_TIMEOUT:
1977                     // We timed out trying to disconnect, force transition to disconnected.
1978                     warn("Disconnecting: Disconnection timeout for " + mCurrentDevice);
1979                     transitionTo(mDisconnected);
1980                     break;
1981 
1982                 case StackEvent.STACK_EVENT:
1983                     StackEvent event = (StackEvent) message.obj;
1984 
1985                     switch (event.type) {
1986                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
1987                             debug(
1988                                     "Disconnecting: Connection state changed: "
1989                                             + event.device
1990                                             + ": "
1991                                             + event.valueInt);
1992                             processConnectionEvent(event.valueInt, event.device);
1993                             break;
1994                         default:
1995                             error("Disconnecting: Unknown stack event: " + event.type);
1996                             break;
1997                     }
1998                     break;
1999                 default:
2000                     warn("Disconnecting: Message not handled " + message);
2001                     return NOT_HANDLED;
2002             }
2003             return HANDLED;
2004         }
2005 
processConnectionEvent(int state, BluetoothDevice device)2006         private void processConnectionEvent(int state, BluetoothDevice device) {
2007             switch (state) {
2008                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
2009                     if (mCurrentDevice.equals(device)) {
2010                         transitionTo(mDisconnected);
2011                     } else {
2012                         error("Disconnecting: Disconnected from unknown device: " + device);
2013                     }
2014                     break;
2015                 default:
2016                     error(
2017                             "Disconnecting: Connection State Device: "
2018                                     + device
2019                                     + " bad state: "
2020                                     + state);
2021                     break;
2022             }
2023         }
2024 
2025         @Override
exit()2026         public void exit() {
2027             debug("Disconnecting: Exit Disconnecting: " + getMessageName(getCurrentMessage().what));
2028             removeMessages(DISCONNECTING_TIMEOUT);
2029             mPrevState = this;
2030         }
2031     }
2032 
2033     class AudioOn extends State {
2034         @Override
enter()2035         public void enter() {
2036             debug("Enter AudioOn: " + getMessageName(getCurrentMessage().what));
2037             broadcastAudioState(
2038                     mCurrentDevice,
2039                     BluetoothHeadsetClient.STATE_AUDIO_CONNECTED,
2040                     BluetoothHeadsetClient.STATE_AUDIO_CONNECTING);
2041         }
2042 
2043         @Override
processMessage(Message message)2044         public synchronized boolean processMessage(Message message) {
2045             debug("AudioOn process message: " + message.what);
2046             if (mCurrentDevice == null) {
2047                 error("ERROR: mCurrentDevice is null in Connected");
2048                 return NOT_HANDLED;
2049             }
2050 
2051             switch (message.what) {
2052                 case DISCONNECT:
2053                     BluetoothDevice device = (BluetoothDevice) message.obj;
2054                     if (!mCurrentDevice.equals(device)) {
2055                         break;
2056                     }
2057                     deferMessage(message);
2058                     /*
2059                      * fall through - disconnect audio first then expect
2060                      * deferred DISCONNECT message in Connected state
2061                      */
2062                 case DISCONNECT_AUDIO:
2063                     /*
2064                      * just disconnect audio and wait for
2065                      * StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED, that triggers State
2066                      * Machines state changing
2067                      */
2068                     if (mNativeInterface.disconnectAudio(mCurrentDevice)) {
2069                         routeHfpAudio(false);
2070                         returnAudioFocusIfNecessary();
2071                     }
2072                     break;
2073 
2074                 case HOLD_CALL:
2075                     holdCall();
2076                     break;
2077 
2078                 case StackEvent.STACK_EVENT:
2079                     StackEvent event = (StackEvent) message.obj;
2080                     debug("AudioOn: event type: " + event.type);
2081                     switch (event.type) {
2082                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
2083                             debug(
2084                                     "AudioOn connection state changed"
2085                                             + event.device
2086                                             + ": "
2087                                             + event.valueInt);
2088                             processConnectionEvent(message, event.valueInt, event.device);
2089                             break;
2090                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
2091                             debug(
2092                                     "AudioOn audio state changed"
2093                                             + event.device
2094                                             + ": "
2095                                             + event.valueInt);
2096                             processAudioEvent(event.valueInt, event.device);
2097                             break;
2098                         default:
2099                             return NOT_HANDLED;
2100                     }
2101                     break;
2102                 default:
2103                     return NOT_HANDLED;
2104             }
2105             return HANDLED;
2106         }
2107 
2108         // in AudioOn state. Can AG disconnect RFCOMM prior to SCO? Handle this
processConnectionEvent(Message message, int state, BluetoothDevice device)2109         private void processConnectionEvent(Message message, int state, BluetoothDevice device) {
2110             switch (state) {
2111                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
2112                     if (mCurrentDevice.equals(device)) {
2113                         processAudioEvent(
2114                                 HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED, device);
2115                         if (Flags.hfpClientDisconnectingState()) {
2116                             transitionTo(mDisconnecting);
2117                             // message is deferred to be processed in the disconnecting state
2118                             deferMessage(message);
2119                         } else {
2120                             transitionTo(mDisconnected);
2121                         }
2122 
2123                     } else {
2124                         error("Disconnected from unknown device: " + device);
2125                     }
2126                     break;
2127                 default:
2128                     error("Connection State Device: " + device + " bad state: " + state);
2129                     break;
2130             }
2131         }
2132 
2133         // in AudioOn state
processAudioEvent(int state, BluetoothDevice device)2134         private void processAudioEvent(int state, BluetoothDevice device) {
2135             if (!mCurrentDevice.equals(device)) {
2136                 error("Audio changed on disconnected device: " + device);
2137                 return;
2138             }
2139 
2140             switch (state) {
2141                 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED:
2142                     removeMessages(DISCONNECT_AUDIO);
2143                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
2144                     // Audio focus may still be held by the entity controlling the actual call
2145                     // (such as Telecom) and hence this will still keep the call around, there
2146                     // is not much we can do here since dropping the call without user consent
2147                     // even if the audio connection snapped may not be a good idea.
2148                     routeHfpAudio(false);
2149                     returnAudioFocusIfNecessary();
2150                     transitionTo(mConnected);
2151                     break;
2152 
2153                 default:
2154                     error("Audio State Device: " + device + " bad state: " + state);
2155                     break;
2156             }
2157         }
2158 
2159         @Override
exit()2160         public void exit() {
2161             debug("Exit AudioOn: " + getMessageName(getCurrentMessage().what));
2162             mPrevState = this;
2163             broadcastAudioState(
2164                     mCurrentDevice,
2165                     BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
2166                     BluetoothHeadsetClient.STATE_AUDIO_CONNECTED);
2167         }
2168     }
2169 
getConnectionState(BluetoothDevice device)2170     public synchronized int getConnectionState(BluetoothDevice device) {
2171         if (device == null || !device.equals(mCurrentDevice)) {
2172             return STATE_DISCONNECTED;
2173         }
2174 
2175         IState currentState = getCurrentState();
2176         if (currentState == mConnecting) {
2177             return STATE_CONNECTING;
2178         }
2179 
2180         if (currentState == mConnected || currentState == mAudioOn) {
2181             return STATE_CONNECTED;
2182         }
2183 
2184         if (Flags.hfpClientDisconnectingState()) {
2185             if (currentState == mDisconnecting) {
2186                 return STATE_DISCONNECTING;
2187             }
2188         }
2189         return STATE_DISCONNECTED;
2190     }
2191 
2192     @VisibleForTesting
broadcastAudioState(BluetoothDevice device, int newState, int prevState)2193     void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
2194         int sco_codec = BluetoothHfpProtoEnums.SCO_CODEC_CVSD;
2195         if (mAudioSWB) {
2196             sco_codec = BluetoothHfpProtoEnums.SCO_CODEC_LC3;
2197         } else if (mAudioWbs) {
2198             sco_codec = BluetoothHfpProtoEnums.SCO_CODEC_MSBC;
2199         }
2200 
2201         BluetoothStatsLog.write(
2202                 BluetoothStatsLog.BLUETOOTH_SCO_CONNECTION_STATE_CHANGED,
2203                 mAdapterService.obfuscateAddress(device),
2204                 getConnectionStateFromAudioState(newState),
2205                 sco_codec,
2206                 mAdapterService.getMetricId(device));
2207         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED);
2208         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
2209         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
2210         if (newState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) {
2211             intent.putExtra(BluetoothHeadsetClient.EXTRA_AUDIO_WBS, mAudioWbs);
2212         }
2213         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2214         mService.sendBroadcast(
2215                 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
2216 
2217         debug("Audio state " + device + ": " + prevState + "->" + newState);
2218         HfpClientConnectionService.onAudioStateChanged(device, newState, prevState);
2219     }
2220 
2221     @VisibleForTesting
processAndroidSlcCommand(String atString, BluetoothDevice device)2222     boolean processAndroidSlcCommand(String atString, BluetoothDevice device) {
2223         if (!mCurrentDevice.equals(device) || atString.lastIndexOf("+ANDROID:") < 0) {
2224             return false;
2225         }
2226 
2227         // Check if it is +ANDROID: (<feature1>,...),(<feature2>, ...) the reply for AT+ANDROID=?
2228         while (true) {
2229             int indexUpperBucket = atString.indexOf("(");
2230             int indexLowerBucket = atString.indexOf(")");
2231 
2232             if (indexUpperBucket < 0
2233                     || indexLowerBucket < 0
2234                     || indexUpperBucket >= indexLowerBucket) {
2235                 break;
2236             }
2237             String feature = atString.substring(indexUpperBucket + 1, indexLowerBucket);
2238             debug("processAndroidSlcCommand: feature=[" + feature + "]");
2239             processAndroidAtFeature(feature.split(","));
2240 
2241             atString = atString.substring(indexLowerBucket + 1);
2242         }
2243         return true;
2244     }
2245 
processAndroidAtFeature(String[] args)2246     private void processAndroidAtFeature(String[] args) {
2247         if (args.length < 1) {
2248             error("processAndroidAtFeature: Invalid feature length");
2249             return;
2250         }
2251 
2252         String featureId = args[0];
2253         if (featureId.equals(BluetoothSinkAudioPolicy.HFP_SET_SINK_AUDIO_POLICY_ID)) {
2254             info(
2255                     "processAndroidAtFeature:"
2256                             + BluetoothSinkAudioPolicy.HFP_SET_SINK_AUDIO_POLICY_ID
2257                             + " supported");
2258             setAudioPolicyRemoteSupported(true);
2259 
2260             // Send default policies to the remote if it supports
2261             if (getForceSetAudioPolicyProperty()) {
2262                 setAudioPolicy(
2263                         new BluetoothSinkAudioPolicy.Builder(mHsClientAudioPolicy)
2264                                 .setActiveDevicePolicyAfterConnection(mConnectingTimePolicyProperty)
2265                                 .setInBandRingtonePolicy(mInBandRingtonePolicyProperty)
2266                                 .build());
2267             }
2268         }
2269     }
2270 
2271     // This method does not check for error condition (newState == prevState)
broadcastConnectionState(BluetoothDevice device, int newState, int prevState)2272     private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
2273         debug("Connection state " + device + ": " + prevState + "->" + newState);
2274         /*
2275          * Notifying the connection state change of the profile before sending
2276          * the intent for connection state change, as it was causing a race
2277          * condition, with the UI not being updated with the correct connection
2278          * state.
2279          */
2280         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
2281         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
2282         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
2283         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2284 
2285         // add feature extras when connected
2286         if (newState == STATE_CONNECTED) {
2287             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY)
2288                     == HeadsetClientHalConstants.PEER_FEAT_3WAY) {
2289                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true);
2290             }
2291             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC)
2292                     == HeadsetClientHalConstants.PEER_FEAT_VREC) {
2293                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true);
2294             }
2295             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT)
2296                     == HeadsetClientHalConstants.PEER_FEAT_REJECT) {
2297                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true);
2298             }
2299             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC)
2300                     == HeadsetClientHalConstants.PEER_FEAT_ECC) {
2301                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true);
2302             }
2303 
2304             // add individual CHLD support extras
2305             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)
2306                     == HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) {
2307                 intent.putExtra(
2308                         BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, true);
2309             }
2310             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL)
2311                     == HeadsetClientHalConstants.CHLD_FEAT_REL) {
2312                 intent.putExtra(
2313                         BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true);
2314             }
2315             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)
2316                     == HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) {
2317                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true);
2318             }
2319             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE)
2320                     == HeadsetClientHalConstants.CHLD_FEAT_MERGE) {
2321                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true);
2322             }
2323             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)
2324                     == HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) {
2325                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true);
2326             }
2327         }
2328 
2329         mService.sendBroadcastMultiplePermissions(
2330                 intent,
2331                 new String[] {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
2332                 Utils.getTempBroadcastOptions());
2333 
2334         HfpClientConnectionService.onConnectionStateChanged(device, newState, prevState);
2335     }
2336 
isConnected()2337     boolean isConnected() {
2338         IState currentState = getCurrentState();
2339         return (currentState == mConnected || currentState == mAudioOn);
2340     }
2341 
getDevicesMatchingConnectionStates(int[] states)2342     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
2343         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
2344         final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
2345         if (bondedDevices == null) {
2346             return deviceList;
2347         }
2348         int connectionState;
2349         synchronized (this) {
2350             for (BluetoothDevice device : bondedDevices) {
2351                 final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
2352                 if (!Utils.arrayContains(featureUuids, BluetoothUuid.HFP_AG)) {
2353                     continue;
2354                 }
2355                 connectionState = getConnectionState(device);
2356                 for (int state : states) {
2357                     if (connectionState == state) {
2358                         deviceList.add(device);
2359                     }
2360                 }
2361             }
2362         }
2363         return deviceList;
2364     }
2365 
okToConnect(BluetoothDevice device)2366     boolean okToConnect(BluetoothDevice device) {
2367         int connectionPolicy = mService.getConnectionPolicy(device);
2368         boolean ret = false;
2369         // check connection policy and accept or reject the connection. if connection policy is
2370         // undefined
2371         // it is likely that our SDP has not completed and peer is initiating
2372         // the
2373         // connection. Allow this connection, provided the device is bonded
2374         if ((CONNECTION_POLICY_FORBIDDEN < connectionPolicy)
2375                 || ((CONNECTION_POLICY_UNKNOWN == connectionPolicy)
2376                         && (mAdapterService.getBondState(device) != BluetoothDevice.BOND_NONE))) {
2377             ret = true;
2378         }
2379         return ret;
2380     }
2381 
isAudioOn()2382     boolean isAudioOn() {
2383         return (getCurrentState() == mAudioOn);
2384     }
2385 
getAudioState(BluetoothDevice device)2386     synchronized int getAudioState(BluetoothDevice device) {
2387         if (mCurrentDevice == null || !mCurrentDevice.equals(device)) {
2388             return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
2389         }
2390         return mAudioState;
2391     }
2392 
getConnectedDevices()2393     List<BluetoothDevice> getConnectedDevices() {
2394         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
2395         synchronized (this) {
2396             if (isConnected()) {
2397                 devices.add(mCurrentDevice);
2398             }
2399         }
2400         return devices;
2401     }
2402 
2403     @VisibleForTesting
getByteAddress(BluetoothDevice device)2404     byte[] getByteAddress(BluetoothDevice device) {
2405         return Utils.getBytesFromAddress(device.getAddress());
2406     }
2407 
getCurrentCalls()2408     public List<HfpClientCall> getCurrentCalls() {
2409         return new ArrayList<HfpClientCall>(mCalls.values());
2410     }
2411 
getCurrentAgEvents()2412     public Bundle getCurrentAgEvents() {
2413         Bundle b = new Bundle();
2414         b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, mIndicatorNetworkState);
2415         b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, mIndicatorNetworkSignal);
2416         b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, mIndicatorNetworkType);
2417         b.putInt(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, mIndicatorBatteryLevel);
2418         b.putString(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, mOperatorName);
2419         b.putString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, mSubscriberInfo);
2420         return b;
2421     }
2422 
2423     @VisibleForTesting
getConnectionStateFromAudioState(int audioState)2424     static int getConnectionStateFromAudioState(int audioState) {
2425         switch (audioState) {
2426             case BluetoothHeadsetClient.STATE_AUDIO_CONNECTED:
2427                 return BluetoothAdapter.STATE_CONNECTED;
2428             case BluetoothHeadsetClient.STATE_AUDIO_CONNECTING:
2429                 return BluetoothAdapter.STATE_CONNECTING;
2430             case BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED:
2431                 return BluetoothAdapter.STATE_DISCONNECTED;
2432         }
2433         return BluetoothAdapter.STATE_DISCONNECTED;
2434     }
2435 
debug(String message)2436     private void debug(String message) {
2437         Log.d(TAG, "[" + mCurrentDevice + "]: " + message);
2438     }
2439 
info(String message)2440     private void info(String message) {
2441         Log.i(TAG, "[" + mCurrentDevice + "]: " + message);
2442     }
2443 
warn(String message)2444     private void warn(String message) {
2445 
2446         Log.w(TAG, "[" + mCurrentDevice + "]: " + message);
2447     }
2448 
error(String message)2449     private void error(String message) {
2450 
2451         Log.e(TAG, "[" + mCurrentDevice + "]: " + message);
2452     }
2453 
setAudioRouteAllowed(boolean allowed)2454     public void setAudioRouteAllowed(boolean allowed) {
2455         mAudioRouteAllowed = allowed;
2456 
2457         int establishPolicy =
2458                 allowed
2459                         ? BluetoothSinkAudioPolicy.POLICY_ALLOWED
2460                         : BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED;
2461 
2462         /*
2463          * Backward compatibility for mAudioRouteAllowed
2464          *
2465          * Set default policies if
2466          *  1. need to set audio policy from system props
2467          *  2. remote device supports audio policy
2468          */
2469         if (getForceSetAudioPolicyProperty()) {
2470             // set call establish policy and connecting policy to POLICY_ALLOWED if allowed=true,
2471             // otherwise set them to the default values
2472             int connectingTimePolicy =
2473                     allowed
2474                             ? BluetoothSinkAudioPolicy.POLICY_ALLOWED
2475                             : getConnectingTimePolicyProperty();
2476 
2477             setAudioPolicy(
2478                     new BluetoothSinkAudioPolicy.Builder(mHsClientAudioPolicy)
2479                             .setCallEstablishPolicy(establishPolicy)
2480                             .setActiveDevicePolicyAfterConnection(connectingTimePolicy)
2481                             .setInBandRingtonePolicy(getInBandRingtonePolicyProperty())
2482                             .build());
2483         } else {
2484             setAudioPolicy(
2485                     new BluetoothSinkAudioPolicy.Builder(mHsClientAudioPolicy)
2486                             .setCallEstablishPolicy(establishPolicy)
2487                             .build());
2488         }
2489     }
2490 
getAudioRouteAllowed()2491     public boolean getAudioRouteAllowed() {
2492         return mAudioRouteAllowed;
2493     }
2494 
createMaskString(BluetoothSinkAudioPolicy policies)2495     private static String createMaskString(BluetoothSinkAudioPolicy policies) {
2496         StringBuilder mask = new StringBuilder();
2497         mask.append(BluetoothSinkAudioPolicy.HFP_SET_SINK_AUDIO_POLICY_ID);
2498         mask.append(",").append(policies.getCallEstablishPolicy());
2499         mask.append(",").append(policies.getActiveDevicePolicyAfterConnection());
2500         mask.append(",").append(policies.getInBandRingtonePolicy());
2501         return mask.toString();
2502     }
2503 
2504     /**
2505      * sets the {@link BluetoothSinkAudioPolicy} object device and send to the remote device using
2506      * Android specific AT commands.
2507      *
2508      * @param policies to be set policies
2509      */
setAudioPolicy(BluetoothSinkAudioPolicy policies)2510     public void setAudioPolicy(BluetoothSinkAudioPolicy policies) {
2511         debug("setAudioPolicy: " + policies);
2512         mHsClientAudioPolicy = policies;
2513 
2514         if (getAudioPolicyRemoteSupported() != BluetoothStatusCodes.FEATURE_SUPPORTED) {
2515             info("Audio Policy feature not supported!");
2516             return;
2517         }
2518 
2519         if (!mNativeInterface.sendAndroidAt(
2520                 mCurrentDevice, "+ANDROID=" + createMaskString(policies))) {
2521             error("ERROR: Couldn't send call audio policies");
2522             return;
2523         }
2524         addQueuedAction(SEND_ANDROID_AT_COMMAND);
2525     }
2526 
queryRemoteSupportedFeatures()2527     private boolean queryRemoteSupportedFeatures() {
2528         info("queryRemoteSupportedFeatures");
2529         if (!mNativeInterface.sendAndroidAt(mCurrentDevice, "+ANDROID=?")) {
2530             error("ERROR: Couldn't send audio policy feature query");
2531             return false;
2532         }
2533         addQueuedAction(SEND_ANDROID_AT_COMMAND);
2534         return true;
2535     }
2536 
2537     /**
2538      * sets the audio policy feature support status
2539      *
2540      * @param supported support status
2541      */
setAudioPolicyRemoteSupported(boolean supported)2542     public void setAudioPolicyRemoteSupported(boolean supported) {
2543         if (supported) {
2544             mAudioPolicyRemoteSupported = BluetoothStatusCodes.FEATURE_SUPPORTED;
2545         } else {
2546             mAudioPolicyRemoteSupported = BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
2547         }
2548     }
2549 
2550     /**
2551      * gets the audio policy feature support status
2552      *
2553      * @return int support status
2554      */
getAudioPolicyRemoteSupported()2555     public int getAudioPolicyRemoteSupported() {
2556         return mAudioPolicyRemoteSupported;
2557     }
2558 
2559     /** handles the value of {@link BluetoothSinkAudioPolicy} from system property */
getAudioPolicySystemProp(String propKey)2560     private static int getAudioPolicySystemProp(String propKey) {
2561         int mProp = SystemProperties.getInt(propKey, BluetoothSinkAudioPolicy.POLICY_UNCONFIGURED);
2562         if (mProp < BluetoothSinkAudioPolicy.POLICY_UNCONFIGURED
2563                 || mProp > BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED) {
2564             mProp = BluetoothSinkAudioPolicy.POLICY_UNCONFIGURED;
2565         }
2566         return mProp;
2567     }
2568 
2569     @VisibleForTesting
getForceSetAudioPolicyProperty()2570     boolean getForceSetAudioPolicyProperty() {
2571         return mForceSetAudioPolicyProperty;
2572     }
2573 
2574     @VisibleForTesting
getConnectingTimePolicyProperty()2575     int getConnectingTimePolicyProperty() {
2576         return mConnectingTimePolicyProperty;
2577     }
2578 
2579     @VisibleForTesting
getInBandRingtonePolicyProperty()2580     int getInBandRingtonePolicyProperty() {
2581         return mInBandRingtonePolicyProperty;
2582     }
2583 }
2584