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