1 /*
2  * Copyright (c) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.hfpclient;
18 
19 import static android.content.pm.PackageManager.FEATURE_WATCH;
20 
21 import android.annotation.RequiresPermission;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothHeadsetClient;
24 import android.bluetooth.BluetoothHeadsetClientCall;
25 import android.bluetooth.BluetoothProfile;
26 import android.bluetooth.BluetoothSinkAudioPolicy;
27 import android.bluetooth.BluetoothStatusCodes;
28 import android.bluetooth.IBluetoothHeadsetClient;
29 import android.content.AttributionSource;
30 import android.content.BroadcastReceiver;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.media.AudioManager;
35 import android.os.BatteryManager;
36 import android.os.Bundle;
37 import android.os.HandlerThread;
38 import android.os.Message;
39 import android.os.SystemProperties;
40 import android.sysprop.BluetoothProperties;
41 import android.util.Log;
42 
43 import com.android.bluetooth.Utils;
44 import com.android.bluetooth.btservice.AdapterService;
45 import com.android.bluetooth.btservice.ProfileService;
46 import com.android.bluetooth.btservice.storage.DatabaseManager;
47 import com.android.internal.annotations.GuardedBy;
48 import com.android.internal.annotations.VisibleForTesting;
49 
50 import java.util.ArrayList;
51 import java.util.Collections;
52 import java.util.HashMap;
53 import java.util.Iterator;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.Objects;
57 import java.util.Set;
58 import java.util.UUID;
59 
60 /**
61  * Provides Bluetooth Headset Client (HF Role) profile, as a service in the Bluetooth application.
62  */
63 public class HeadsetClientService extends ProfileService {
64     private static final String TAG = HeadsetClientService.class.getSimpleName();
65 
66     // This is also used as a lock for shared data in {@link HeadsetClientService}
67     @GuardedBy("mStateMachineMap")
68     private final HashMap<BluetoothDevice, HeadsetClientStateMachine> mStateMachineMap =
69             new HashMap<>();
70 
71     private static HeadsetClientService sHeadsetClientService;
72     private NativeInterface mNativeInterface = null;
73     private HandlerThread mSmThread = null;
74     private HeadsetClientStateMachineFactory mSmFactory = null;
75     private DatabaseManager mDatabaseManager;
76     private AudioManager mAudioManager = null;
77     private BatteryManager mBatteryManager = null;
78     private int mLastBatteryLevel = -1;
79     // Maxinum number of devices we can try connecting to in one session
80     private static final int MAX_STATE_MACHINES_POSSIBLE = 100;
81 
82     private final Object mStartStopLock = new Object();
83 
84     public static final String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag";
85 
HeadsetClientService(Context ctx)86     public HeadsetClientService(Context ctx) {
87         super(ctx);
88     }
89 
isEnabled()90     public static boolean isEnabled() {
91         return BluetoothProperties.isProfileHfpHfEnabled().orElse(false);
92     }
93 
94     @Override
initBinder()95     public IProfileServiceBinder initBinder() {
96         return new BluetoothHeadsetClientBinder(this);
97     }
98 
99     @Override
start()100     public void start() {
101         synchronized (mStartStopLock) {
102             Log.d(TAG, "start()");
103             if (getHeadsetClientService() != null) {
104                 throw new IllegalStateException("start() called twice");
105             }
106 
107             mDatabaseManager =
108                     Objects.requireNonNull(
109                             AdapterService.getAdapterService().getDatabase(),
110                             "DatabaseManager cannot be null when HeadsetClientService starts");
111 
112             // Setup the JNI service
113             mNativeInterface = NativeInterface.getInstance();
114             mNativeInterface.initialize();
115 
116             mBatteryManager = getSystemService(BatteryManager.class);
117 
118             mAudioManager = getSystemService(AudioManager.class);
119             if (mAudioManager == null) {
120                 Log.e(TAG, "AudioManager service doesn't exist?");
121             } else {
122                 // start AudioManager in a known state
123                 mAudioManager.setHfpEnabled(false);
124             }
125 
126             mSmFactory = new HeadsetClientStateMachineFactory();
127             synchronized (mStateMachineMap) {
128                 mStateMachineMap.clear();
129             }
130 
131             IntentFilter filter = new IntentFilter(AudioManager.ACTION_VOLUME_CHANGED);
132             filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
133             filter.addAction(Intent.ACTION_BATTERY_CHANGED);
134             registerReceiver(mBroadcastReceiver, filter);
135 
136             // Start the HfpClientConnectionService to create connection with telecom when HFP
137             // connection is available on non-wearable device.
138             if (getPackageManager() != null
139                     && !getPackageManager().hasSystemFeature(FEATURE_WATCH)) {
140                 Intent startIntent = new Intent(this, HfpClientConnectionService.class);
141                 startService(startIntent);
142             }
143 
144             // Create the thread on which all State Machines will run
145             mSmThread = new HandlerThread("HeadsetClient.SM");
146             mSmThread.start();
147 
148             setHeadsetClientService(this);
149         }
150     }
151 
152     @Override
stop()153     public void stop() {
154         synchronized (mStartStopLock) {
155             synchronized (HeadsetClientService.class) {
156                 if (sHeadsetClientService == null) {
157                     Log.w(TAG, "stop() called without start()");
158                     return;
159                 }
160 
161                 // Stop the HfpClientConnectionService for non-wearables devices.
162                 if (getPackageManager() != null
163                         && !getPackageManager().hasSystemFeature(FEATURE_WATCH)) {
164                     Intent stopIntent = new Intent(this, HfpClientConnectionService.class);
165                     sHeadsetClientService.stopService(stopIntent);
166                 }
167             }
168 
169             setHeadsetClientService(null);
170 
171             unregisterReceiver(mBroadcastReceiver);
172 
173             synchronized (mStateMachineMap) {
174                 for (Iterator<Map.Entry<BluetoothDevice, HeadsetClientStateMachine>> it =
175                                 mStateMachineMap.entrySet().iterator();
176                         it.hasNext(); ) {
177                     HeadsetClientStateMachine sm =
178                             mStateMachineMap.get((BluetoothDevice) it.next().getKey());
179                     sm.doQuit();
180                     it.remove();
181                 }
182             }
183 
184             // Stop the handler thread
185             mSmThread.quit();
186             mSmThread = null;
187 
188             mNativeInterface.cleanup();
189             mNativeInterface = null;
190         }
191     }
192 
193     private final BroadcastReceiver mBroadcastReceiver =
194             new BroadcastReceiver() {
195                 @Override
196                 public void onReceive(Context context, Intent intent) {
197                     String action = intent.getAction();
198 
199                     // We handle the volume changes for Voice calls here since HFP audio volume
200                     // control does
201                     // not go through audio manager (audio mixer). see
202                     // ({@link HeadsetClientStateMachine#SET_SPEAKER_VOLUME} in
203                     // {@link HeadsetClientStateMachine} for details.
204                     if (action.equals(AudioManager.ACTION_VOLUME_CHANGED)) {
205                         Log.d(
206                                 TAG,
207                                 "Volume changed for stream: "
208                                         + intent.getIntExtra(
209                                                 AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1));
210                         int streamType =
211                                 intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
212                         if (streamType == AudioManager.STREAM_VOICE_CALL) {
213                             int streamValue =
214                                     intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
215                             int hfVol = HeadsetClientStateMachine.amToHfVol(streamValue);
216                             Log.d(
217                                     TAG,
218                                     "Setting volume to audio manager: "
219                                             + streamValue
220                                             + " hands free: "
221                                             + hfVol);
222                             mAudioManager.setHfpVolume(hfVol);
223                             synchronized (mStateMachineMap) {
224                                 for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
225                                     if (sm != null) {
226                                         sm.sendMessage(
227                                                 HeadsetClientStateMachine.SET_SPEAKER_VOLUME,
228                                                 streamValue);
229                                     }
230                                 }
231                             }
232                         }
233                     } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
234                         int batteryIndicatorID = 2;
235                         int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
236 
237                         if (batteryLevel == mLastBatteryLevel) {
238                             return;
239                         }
240                         mLastBatteryLevel = batteryLevel;
241 
242                         Log.d(
243                                 TAG,
244                                 "Send battery level update BIEV(2," + batteryLevel + ") command");
245 
246                         synchronized (mStateMachineMap) {
247                             for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
248                                 if (sm != null) {
249                                     sm.sendMessage(
250                                             HeadsetClientStateMachine.SEND_BIEV,
251                                             batteryIndicatorID,
252                                             batteryLevel);
253                                 }
254                             }
255                         }
256                     }
257                 }
258             };
259 
260     /**
261      * Convert {@code HfpClientCall} to legacy {@code BluetoothHeadsetClientCall} still used by some
262      * clients.
263      */
toLegacyCall(HfpClientCall call)264     static BluetoothHeadsetClientCall toLegacyCall(HfpClientCall call) {
265         if (call == null) return null;
266         return new BluetoothHeadsetClientCall(
267                 call.getDevice(),
268                 call.getId(),
269                 call.getUUID(),
270                 call.getState(),
271                 call.getNumber(),
272                 call.isMultiParty(),
273                 call.isOutgoing(),
274                 call.isInBandRing());
275     }
276 
277     /** Handlers for incoming service calls */
278     @VisibleForTesting
279     static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
280             implements IProfileServiceBinder {
281         private HeadsetClientService mService;
282 
BluetoothHeadsetClientBinder(HeadsetClientService svc)283         BluetoothHeadsetClientBinder(HeadsetClientService svc) {
284             mService = svc;
285         }
286 
287         @Override
cleanup()288         public void cleanup() {
289             mService = null;
290         }
291 
292         @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getService(AttributionSource source)293         private HeadsetClientService getService(AttributionSource source) {
294             if (Utils.isInstrumentationTestMode()) {
295                 return mService;
296             }
297             if (!Utils.checkServiceAvailable(mService, TAG)
298                     || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
299                     || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
300                 return null;
301             }
302             return mService;
303         }
304 
305         @Override
connect(BluetoothDevice device, AttributionSource source)306         public boolean connect(BluetoothDevice device, AttributionSource source) {
307             HeadsetClientService service = getService(source);
308             if (service == null) {
309                 return false;
310             }
311 
312             return service.connect(device);
313         }
314 
315         @Override
disconnect(BluetoothDevice device, AttributionSource source)316         public boolean disconnect(BluetoothDevice device, AttributionSource source) {
317             HeadsetClientService service = getService(source);
318             if (service == null) {
319                 return false;
320             }
321 
322             return service.disconnect(device);
323         }
324 
325         @Override
getConnectedDevices(AttributionSource source)326         public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
327             HeadsetClientService service = getService(source);
328             if (service == null) {
329                 return Collections.emptyList();
330             }
331 
332             return service.getConnectedDevices();
333         }
334 
335         @Override
getDevicesMatchingConnectionStates( int[] states, AttributionSource source)336         public List<BluetoothDevice> getDevicesMatchingConnectionStates(
337                 int[] states, AttributionSource source) {
338             HeadsetClientService service = getService(source);
339             if (service == null) {
340                 return Collections.emptyList();
341             }
342 
343             return service.getDevicesMatchingConnectionStates(states);
344         }
345 
346         @Override
getConnectionState(BluetoothDevice device, AttributionSource source)347         public int getConnectionState(BluetoothDevice device, AttributionSource source) {
348             HeadsetClientService service = getService(source);
349             if (service == null) {
350                 return BluetoothProfile.STATE_DISCONNECTED;
351             }
352 
353             return service.getConnectionState(device);
354         }
355 
356         @Override
setConnectionPolicy( BluetoothDevice device, int connectionPolicy, AttributionSource source)357         public boolean setConnectionPolicy(
358                 BluetoothDevice device, int connectionPolicy, AttributionSource source) {
359             HeadsetClientService service = getService(source);
360             if (service == null) {
361                 return false;
362             }
363 
364             return service.setConnectionPolicy(device, connectionPolicy);
365         }
366 
367         @Override
getConnectionPolicy(BluetoothDevice device, AttributionSource source)368         public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
369             HeadsetClientService service = getService(source);
370             if (service == null) {
371                 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
372             }
373 
374             return service.getConnectionPolicy(device);
375         }
376 
377         @Override
startVoiceRecognition(BluetoothDevice device, AttributionSource source)378         public boolean startVoiceRecognition(BluetoothDevice device, AttributionSource source) {
379             HeadsetClientService service = getService(source);
380             if (service == null) {
381                 return false;
382             }
383 
384             return service.startVoiceRecognition(device);
385         }
386 
387         @Override
stopVoiceRecognition(BluetoothDevice device, AttributionSource source)388         public boolean stopVoiceRecognition(BluetoothDevice device, AttributionSource source) {
389             HeadsetClientService service = getService(source);
390             if (service == null) {
391                 return false;
392             }
393 
394             return service.stopVoiceRecognition(device);
395         }
396 
397         @Override
getAudioState(BluetoothDevice device, AttributionSource source)398         public int getAudioState(BluetoothDevice device, AttributionSource source) {
399             HeadsetClientService service = getService(source);
400             if (service == null) {
401                 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
402             }
403 
404             return service.getAudioState(device);
405         }
406 
407         @Override
setAudioRouteAllowed( BluetoothDevice device, boolean allowed, AttributionSource source)408         public void setAudioRouteAllowed(
409                 BluetoothDevice device, boolean allowed, AttributionSource source) {
410             HeadsetClientService service = getService(source);
411             if (service == null) {
412                 Log.w(TAG, "Service handle is null for setAudioRouteAllowed!");
413                 return;
414             }
415 
416             service.setAudioRouteAllowed(device, allowed);
417         }
418 
419         @Override
getAudioRouteAllowed(BluetoothDevice device, AttributionSource source)420         public boolean getAudioRouteAllowed(BluetoothDevice device, AttributionSource source) {
421             HeadsetClientService service = getService(source);
422             if (service == null) {
423                 Log.w(TAG, "Service handle is null for getAudioRouteAllowed!");
424                 return false;
425             }
426 
427             return service.getAudioRouteAllowed(device);
428         }
429 
430         @Override
connectAudio(BluetoothDevice device, AttributionSource source)431         public boolean connectAudio(BluetoothDevice device, AttributionSource source) {
432             HeadsetClientService service = getService(source);
433             if (service == null) {
434                 return false;
435             }
436 
437             return service.connectAudio(device);
438         }
439 
440         @Override
disconnectAudio(BluetoothDevice device, AttributionSource source)441         public boolean disconnectAudio(BluetoothDevice device, AttributionSource source) {
442             HeadsetClientService service = getService(source);
443             if (service == null) {
444                 return false;
445             }
446 
447             return service.disconnectAudio(device);
448         }
449 
450         @Override
acceptCall(BluetoothDevice device, int flag, AttributionSource source)451         public boolean acceptCall(BluetoothDevice device, int flag, AttributionSource source) {
452             HeadsetClientService service = getService(source);
453             if (service == null) {
454                 return false;
455             }
456 
457             return service.acceptCall(device, flag);
458         }
459 
460         @Override
rejectCall(BluetoothDevice device, AttributionSource source)461         public boolean rejectCall(BluetoothDevice device, AttributionSource source) {
462             HeadsetClientService service = getService(source);
463             if (service == null) {
464                 return false;
465             }
466 
467             return service.rejectCall(device);
468         }
469 
470         @Override
holdCall(BluetoothDevice device, AttributionSource source)471         public boolean holdCall(BluetoothDevice device, AttributionSource source) {
472             HeadsetClientService service = getService(source);
473             if (service == null) {
474                 return false;
475             }
476 
477             return service.holdCall(device);
478         }
479 
480         @Override
terminateCall( BluetoothDevice device, BluetoothHeadsetClientCall call, AttributionSource source)481         public boolean terminateCall(
482                 BluetoothDevice device, BluetoothHeadsetClientCall call, AttributionSource source) {
483             HeadsetClientService service = getService(source);
484             if (service == null) {
485                 Log.w(TAG, "service is null");
486                 return false;
487             }
488 
489             return service.terminateCall(device, call != null ? call.getUUID() : null);
490         }
491 
492         @Override
explicitCallTransfer(BluetoothDevice device, AttributionSource source)493         public boolean explicitCallTransfer(BluetoothDevice device, AttributionSource source) {
494             HeadsetClientService service = getService(source);
495             if (service == null) {
496                 return false;
497             }
498 
499             return service.explicitCallTransfer(device);
500         }
501 
502         @Override
enterPrivateMode( BluetoothDevice device, int index, AttributionSource source)503         public boolean enterPrivateMode(
504                 BluetoothDevice device, int index, AttributionSource source) {
505             HeadsetClientService service = getService(source);
506             if (service == null) {
507                 return false;
508             }
509 
510             return service.enterPrivateMode(device, index);
511         }
512 
513         @Override
dial( BluetoothDevice device, String number, AttributionSource source)514         public BluetoothHeadsetClientCall dial(
515                 BluetoothDevice device, String number, AttributionSource source) {
516             HeadsetClientService service = getService(source);
517             if (service == null) {
518                 return null;
519             }
520 
521             return toLegacyCall(service.dial(device, number));
522         }
523 
524         @Override
getCurrentCalls( BluetoothDevice device, AttributionSource source)525         public List<BluetoothHeadsetClientCall> getCurrentCalls(
526                 BluetoothDevice device, AttributionSource source) {
527             HeadsetClientService service = getService(source);
528             List<BluetoothHeadsetClientCall> currentCalls = new ArrayList<>();
529             if (service == null) {
530                 return currentCalls;
531             }
532 
533             List<HfpClientCall> calls = service.getCurrentCalls(device);
534             if (calls != null) {
535                 for (HfpClientCall call : calls) {
536                     currentCalls.add(toLegacyCall(call));
537                 }
538             }
539             return currentCalls;
540         }
541 
542         @Override
sendDTMF(BluetoothDevice device, byte code, AttributionSource source)543         public boolean sendDTMF(BluetoothDevice device, byte code, AttributionSource source) {
544             HeadsetClientService service = getService(source);
545             if (service == null) {
546                 return false;
547             }
548 
549             return service.sendDTMF(device, code);
550         }
551 
552         @Override
getLastVoiceTagNumber(BluetoothDevice device, AttributionSource source)553         public boolean getLastVoiceTagNumber(BluetoothDevice device, AttributionSource source) {
554             HeadsetClientService service = getService(source);
555             if (service == null) {
556                 return false;
557             }
558 
559             return service.getLastVoiceTagNumber(device);
560         }
561 
562         @Override
getCurrentAgEvents(BluetoothDevice device, AttributionSource source)563         public Bundle getCurrentAgEvents(BluetoothDevice device, AttributionSource source) {
564             HeadsetClientService service = getService(source);
565             if (service == null) {
566                 return null;
567             }
568 
569             return service.getCurrentAgEvents(device);
570         }
571 
572         @Override
sendVendorAtCommand( BluetoothDevice device, int vendorId, String atCommand, AttributionSource source)573         public boolean sendVendorAtCommand(
574                 BluetoothDevice device, int vendorId, String atCommand, AttributionSource source) {
575             HeadsetClientService service = getService(source);
576             if (service == null) {
577                 return false;
578             }
579 
580             return service.sendVendorAtCommand(device, vendorId, atCommand);
581         }
582 
583         @Override
getCurrentAgFeatures(BluetoothDevice device, AttributionSource source)584         public Bundle getCurrentAgFeatures(BluetoothDevice device, AttributionSource source) {
585             HeadsetClientService service = getService(source);
586             if (service == null) {
587                 return null;
588             }
589 
590             return service.getCurrentAgFeaturesBundle(device);
591         }
592     }
593 
594     // API methods
getHeadsetClientService()595     public static synchronized HeadsetClientService getHeadsetClientService() {
596         if (sHeadsetClientService == null) {
597             Log.w(TAG, "getHeadsetClientService(): service is null");
598             return null;
599         }
600         if (!sHeadsetClientService.isAvailable()) {
601             Log.w(TAG, "getHeadsetClientService(): service is not available ");
602             return null;
603         }
604         return sHeadsetClientService;
605     }
606 
607     /** Set a {@link HeadsetClientService} instance. */
608     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
setHeadsetClientService(HeadsetClientService instance)609     public static synchronized void setHeadsetClientService(HeadsetClientService instance) {
610         Log.d(TAG, "setHeadsetClientService(): set to: " + instance);
611         sHeadsetClientService = instance;
612     }
613 
connect(BluetoothDevice device)614     public boolean connect(BluetoothDevice device) {
615         Log.d(TAG, "connect " + device);
616         if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
617             Log.w(
618                     TAG,
619                     "Connection not allowed: <"
620                             + device.getAddress()
621                             + "> is CONNECTION_POLICY_FORBIDDEN");
622             return false;
623         }
624         HeadsetClientStateMachine sm = getStateMachine(device, true);
625         if (sm == null) {
626             Log.e(TAG, "Cannot allocate SM for device " + device);
627             return false;
628         }
629 
630         sm.sendMessage(HeadsetClientStateMachine.CONNECT, device);
631         return true;
632     }
633 
634     /**
635      * Disconnects hfp client for the remote bluetooth device
636      *
637      * @param device is the device with which we are attempting to disconnect the profile
638      * @return true if hfp client profile successfully disconnected, false otherwise
639      */
disconnect(BluetoothDevice device)640     public boolean disconnect(BluetoothDevice device) {
641         HeadsetClientStateMachine sm = getStateMachine(device);
642         if (sm == null) {
643             Log.e(TAG, "SM does not exist for device " + device);
644             return false;
645         }
646 
647         int connectionState = sm.getConnectionState(device);
648         if (connectionState != BluetoothProfile.STATE_CONNECTED
649                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
650             return false;
651         }
652 
653         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
654         return true;
655     }
656 
657     /**
658      * @return A list of connected {@link BluetoothDevice}.
659      */
getConnectedDevices()660     public List<BluetoothDevice> getConnectedDevices() {
661         ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>();
662         synchronized (mStateMachineMap) {
663             for (BluetoothDevice bd : mStateMachineMap.keySet()) {
664                 HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
665                 if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) {
666                     connectedDevices.add(bd);
667                 }
668             }
669         }
670         return connectedDevices;
671     }
672 
getDevicesMatchingConnectionStates(int[] states)673     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
674         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
675         synchronized (mStateMachineMap) {
676             for (BluetoothDevice bd : mStateMachineMap.keySet()) {
677                 for (int state : states) {
678                     HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
679                     if (sm != null && sm.getConnectionState(bd) == state) {
680                         devices.add(bd);
681                     }
682                 }
683             }
684         }
685         return devices;
686     }
687 
688     /**
689      * Get the current connection state of the profile
690      *
691      * @param device is the remote bluetooth device
692      * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected, {@link
693      *     BluetoothProfile#STATE_CONNECTING} if this profile is being connected, {@link
694      *     BluetoothProfile#STATE_CONNECTED} if this profile is connected, or {@link
695      *     BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected
696      */
getConnectionState(BluetoothDevice device)697     public int getConnectionState(BluetoothDevice device) {
698         HeadsetClientStateMachine sm = getStateMachine(device);
699         if (sm != null) {
700             return sm.getConnectionState(device);
701         }
702 
703         return BluetoothProfile.STATE_DISCONNECTED;
704     }
705 
706     /**
707      * Set connection policy of the profile and connects it if connectionPolicy is {@link
708      * BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is {@link
709      * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
710      *
711      * <p>The device should already be paired. Connection policy can be one of: {@link
712      * BluetoothProfile#CONNECTION_POLICY_ALLOWED}, {@link
713      * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link
714      * BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
715      *
716      * @param device Paired bluetooth device
717      * @param connectionPolicy is the connection policy to set to for this profile
718      * @return true if connectionPolicy is set, false on error
719      */
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)720     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
721         Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
722 
723         if (!mDatabaseManager.setProfileConnectionPolicy(
724                 device, BluetoothProfile.HEADSET_CLIENT, connectionPolicy)) {
725             return false;
726         }
727         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
728             connect(device);
729         } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
730             disconnect(device);
731         }
732         return true;
733     }
734 
735     /**
736      * Get the connection policy of the profile.
737      *
738      * <p>The connection policy can be any of: {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
739      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link
740      * BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
741      *
742      * @param device Bluetooth device
743      * @return connection policy of the device
744      */
getConnectionPolicy(BluetoothDevice device)745     public int getConnectionPolicy(BluetoothDevice device) {
746         return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET_CLIENT);
747     }
748 
startVoiceRecognition(BluetoothDevice device)749     boolean startVoiceRecognition(BluetoothDevice device) {
750         HeadsetClientStateMachine sm = getStateMachine(device);
751         if (sm == null) {
752             Log.e(TAG, "SM does not exist for device " + device);
753             return false;
754         }
755         int connectionState = sm.getConnectionState(device);
756         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
757             return false;
758         }
759         sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START);
760         return true;
761     }
762 
stopVoiceRecognition(BluetoothDevice device)763     boolean stopVoiceRecognition(BluetoothDevice device) {
764         HeadsetClientStateMachine sm = getStateMachine(device);
765         if (sm == null) {
766             Log.e(TAG, "SM does not exist for device " + device);
767             return false;
768         }
769         int connectionState = sm.getConnectionState(device);
770         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
771             return false;
772         }
773         sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_STOP);
774         return true;
775     }
776 
777     /**
778      * Gets audio state of the connection with {@code device}.
779      *
780      * <p>Can be one of {@link STATE_AUDIO_CONNECTED}, {@link STATE_AUDIO_CONNECTING}, or {@link
781      * STATE_AUDIO_DISCONNECTED}.
782      */
getAudioState(BluetoothDevice device)783     public int getAudioState(BluetoothDevice device) {
784         HeadsetClientStateMachine sm = getStateMachine(device);
785         if (sm == null) {
786             Log.e(TAG, "SM does not exist for device " + device);
787             return -1;
788         }
789 
790         return sm.getAudioState(device);
791     }
792 
setAudioRouteAllowed(BluetoothDevice device, boolean allowed)793     public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
794         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
795         Log.i(
796                 TAG,
797                 "setAudioRouteAllowed: device="
798                         + device
799                         + ", allowed="
800                         + allowed
801                         + ", "
802                         + Utils.getUidPidString());
803         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
804         if (sm != null) {
805             sm.setAudioRouteAllowed(allowed);
806         }
807     }
808 
getAudioRouteAllowed(BluetoothDevice device)809     public boolean getAudioRouteAllowed(BluetoothDevice device) {
810         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
811         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
812         if (sm != null) {
813             return sm.getAudioRouteAllowed();
814         }
815         return false;
816     }
817 
818     /**
819      * sends the {@link BluetoothSinkAudioPolicy} object to the state machine of the corresponding
820      * device to store and send to the remote device using Android specific AT commands.
821      *
822      * @param device for whom the policies to be set
823      * @param policies to be set policies
824      */
setAudioPolicy(BluetoothDevice device, BluetoothSinkAudioPolicy policies)825     public void setAudioPolicy(BluetoothDevice device, BluetoothSinkAudioPolicy policies) {
826         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
827         Log.i(
828                 TAG,
829                 "setAudioPolicy: device="
830                         + device
831                         + ", "
832                         + policies.toString()
833                         + ", "
834                         + Utils.getUidPidString());
835         HeadsetClientStateMachine sm = getStateMachine(device);
836         if (sm != null) {
837             sm.setAudioPolicy(policies);
838         }
839     }
840 
841     /**
842      * sets the audio policy feature support status for the corresponding device.
843      *
844      * @param device for whom the policies to be set
845      * @param supported support status
846      */
setAudioPolicyRemoteSupported(BluetoothDevice device, boolean supported)847     public void setAudioPolicyRemoteSupported(BluetoothDevice device, boolean supported) {
848         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
849         Log.i(TAG, "setAudioPolicyRemoteSupported: " + supported);
850         HeadsetClientStateMachine sm = getStateMachine(device);
851         if (sm != null) {
852             sm.setAudioPolicyRemoteSupported(supported);
853         }
854     }
855 
856     /**
857      * gets the audio policy feature support status for the corresponding device.
858      *
859      * @param device for whom the policies to be set
860      * @return int support status
861      */
getAudioPolicyRemoteSupported(BluetoothDevice device)862     public int getAudioPolicyRemoteSupported(BluetoothDevice device) {
863         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
864         HeadsetClientStateMachine sm = getStateMachine(device);
865         if (sm != null) {
866             return sm.getAudioPolicyRemoteSupported();
867         }
868         return BluetoothStatusCodes.FEATURE_NOT_CONFIGURED;
869     }
870 
connectAudio(BluetoothDevice device)871     public boolean connectAudio(BluetoothDevice device) {
872         Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString());
873         HeadsetClientStateMachine sm = getStateMachine(device);
874         if (sm == null) {
875             Log.e(TAG, "SM does not exist for device " + device);
876             return false;
877         }
878 
879         if (!sm.isConnected()) {
880             return false;
881         }
882         if (sm.isAudioOn()) {
883             return false;
884         }
885         sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
886         return true;
887     }
888 
disconnectAudio(BluetoothDevice device)889     public boolean disconnectAudio(BluetoothDevice device) {
890         HeadsetClientStateMachine sm = getStateMachine(device);
891         if (sm == null) {
892             Log.e(TAG, "SM does not exist for device " + device);
893             return false;
894         }
895 
896         if (!sm.isAudioOn()) {
897             return false;
898         }
899         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
900         return true;
901     }
902 
holdCall(BluetoothDevice device)903     public boolean holdCall(BluetoothDevice device) {
904         HeadsetClientStateMachine sm = getStateMachine(device);
905         if (sm == null) {
906             Log.e(TAG, "SM does not exist for device " + device);
907             return false;
908         }
909 
910         int connectionState = sm.getConnectionState(device);
911         if (connectionState != BluetoothProfile.STATE_CONNECTED
912                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
913             return false;
914         }
915         Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
916         sm.sendMessage(msg);
917         return true;
918     }
919 
acceptCall(BluetoothDevice device, int flag)920     public boolean acceptCall(BluetoothDevice device, int flag) {
921         /* Phonecalls from a single device are supported, hang up any calls on the other phone */
922         synchronized (mStateMachineMap) {
923             for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry :
924                     mStateMachineMap.entrySet()) {
925                 if (entry.getValue() == null || entry.getKey().equals(device)) {
926                     continue;
927                 }
928                 int connectionState = entry.getValue().getConnectionState(entry.getKey());
929                 Log.d(
930                         TAG,
931                         "Accepting a call on device "
932                                 + device
933                                 + ". Possibly disconnecting on "
934                                 + entry.getValue());
935                 if (connectionState == BluetoothProfile.STATE_CONNECTED) {
936                     entry.getValue()
937                             .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL)
938                             .sendToTarget();
939                 }
940             }
941         }
942         HeadsetClientStateMachine sm = getStateMachine(device);
943         if (sm == null) {
944             Log.e(TAG, "SM does not exist for device " + device);
945             return false;
946         }
947 
948         int connectionState = sm.getConnectionState(device);
949         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
950             return false;
951         }
952         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
953         msg.arg1 = flag;
954         sm.sendMessage(msg);
955         return true;
956     }
957 
rejectCall(BluetoothDevice device)958     public boolean rejectCall(BluetoothDevice device) {
959         HeadsetClientStateMachine sm = getStateMachine(device);
960         if (sm == null) {
961             Log.e(TAG, "SM does not exist for device " + device);
962             return false;
963         }
964 
965         int connectionState = sm.getConnectionState(device);
966         if (connectionState != BluetoothProfile.STATE_CONNECTED
967                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
968             return false;
969         }
970 
971         Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
972         sm.sendMessage(msg);
973         return true;
974     }
975 
terminateCall(BluetoothDevice device, UUID uuid)976     public boolean terminateCall(BluetoothDevice device, UUID uuid) {
977         HeadsetClientStateMachine sm = getStateMachine(device);
978         if (sm == null) {
979             Log.e(TAG, "SM does not exist for device " + device);
980             return false;
981         }
982 
983         int connectionState = sm.getConnectionState(device);
984         if (connectionState != BluetoothProfile.STATE_CONNECTED
985                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
986             return false;
987         }
988 
989         Message msg = sm.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
990         msg.obj = uuid;
991         sm.sendMessage(msg);
992         return true;
993     }
994 
enterPrivateMode(BluetoothDevice device, int index)995     public boolean enterPrivateMode(BluetoothDevice device, int index) {
996         HeadsetClientStateMachine sm = getStateMachine(device);
997         if (sm == null) {
998             Log.e(TAG, "SM does not exist for device " + device);
999             return false;
1000         }
1001 
1002         int connectionState = sm.getConnectionState(device);
1003         if (connectionState != BluetoothProfile.STATE_CONNECTED
1004                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
1005             return false;
1006         }
1007 
1008         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
1009         msg.arg1 = index;
1010         sm.sendMessage(msg);
1011         return true;
1012     }
1013 
dial(BluetoothDevice device, String number)1014     public HfpClientCall dial(BluetoothDevice device, String number) {
1015         HeadsetClientStateMachine sm = getStateMachine(device);
1016         if (sm == null) {
1017             Log.e(TAG, "SM does not exist for device " + device);
1018             return null;
1019         }
1020 
1021         int connectionState = sm.getConnectionState(device);
1022         if (connectionState != BluetoothProfile.STATE_CONNECTED
1023                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
1024             return null;
1025         }
1026 
1027         // Some platform does not support three way calling (ex: watch)
1028         final boolean support_three_way_calling =
1029                 SystemProperties.getBoolean(
1030                         "bluetooth.headset_client.three_way_calling.enabled", true);
1031         if (!support_three_way_calling && !getCurrentCalls(device).isEmpty()) {
1032             Log.e(TAG, String.format("dial(%s): Line is busy, reject dialing", device));
1033             return null;
1034         }
1035 
1036         HfpClientCall call =
1037                 new HfpClientCall(
1038                         device,
1039                         HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID,
1040                         HfpClientCall.CALL_STATE_DIALING,
1041                         number,
1042                         false /* multiparty */,
1043                         true /* outgoing */,
1044                         sm.getInBandRing());
1045         Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
1046         msg.obj = call;
1047         sm.sendMessage(msg);
1048         return call;
1049     }
1050 
sendDTMF(BluetoothDevice device, byte code)1051     public boolean sendDTMF(BluetoothDevice device, byte code) {
1052         HeadsetClientStateMachine sm = getStateMachine(device);
1053         if (sm == null) {
1054             Log.e(TAG, "SM does not exist for device " + device);
1055             return false;
1056         }
1057 
1058         int connectionState = sm.getConnectionState(device);
1059         if (connectionState != BluetoothProfile.STATE_CONNECTED
1060                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
1061             return false;
1062         }
1063         Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
1064         msg.arg1 = code;
1065         sm.sendMessage(msg);
1066         return true;
1067     }
1068 
getLastVoiceTagNumber(BluetoothDevice device)1069     public boolean getLastVoiceTagNumber(BluetoothDevice device) {
1070         return false;
1071     }
1072 
getCurrentCalls(BluetoothDevice device)1073     public List<HfpClientCall> getCurrentCalls(BluetoothDevice device) {
1074         HeadsetClientStateMachine sm = getStateMachine(device);
1075         if (sm == null) {
1076             Log.e(TAG, "SM does not exist for device " + device);
1077             return null;
1078         }
1079 
1080         int connectionState = sm.getConnectionState(device);
1081         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
1082             return null;
1083         }
1084         return sm.getCurrentCalls();
1085     }
1086 
explicitCallTransfer(BluetoothDevice device)1087     public boolean explicitCallTransfer(BluetoothDevice device) {
1088         HeadsetClientStateMachine sm = getStateMachine(device);
1089         if (sm == null) {
1090             Log.e(TAG, "SM does not exist for device " + device);
1091             return false;
1092         }
1093 
1094         int connectionState = sm.getConnectionState(device);
1095         if (connectionState != BluetoothProfile.STATE_CONNECTED
1096                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
1097             return false;
1098         }
1099         Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
1100         sm.sendMessage(msg);
1101         return true;
1102     }
1103 
1104     /** Send vendor AT command. */
sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand)1105     public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) {
1106         HeadsetClientStateMachine sm = getStateMachine(device);
1107         if (sm == null) {
1108             Log.e(TAG, "SM does not exist for device " + device);
1109             return false;
1110         }
1111 
1112         int connectionState = sm.getConnectionState(device);
1113         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
1114             return false;
1115         }
1116 
1117         Message msg =
1118                 sm.obtainMessage(
1119                         HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, vendorId, 0, atCommand);
1120         sm.sendMessage(msg);
1121         return true;
1122     }
1123 
getCurrentAgEvents(BluetoothDevice device)1124     public Bundle getCurrentAgEvents(BluetoothDevice device) {
1125         HeadsetClientStateMachine sm = getStateMachine(device);
1126         if (sm == null) {
1127             Log.e(TAG, "SM does not exist for device " + device);
1128             return null;
1129         }
1130 
1131         int connectionState = sm.getConnectionState(device);
1132         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
1133             return null;
1134         }
1135         return sm.getCurrentAgEvents();
1136     }
1137 
getCurrentAgFeaturesBundle(BluetoothDevice device)1138     public Bundle getCurrentAgFeaturesBundle(BluetoothDevice device) {
1139         HeadsetClientStateMachine sm = getStateMachine(device);
1140         if (sm == null) {
1141             Log.e(TAG, "SM does not exist for device " + device);
1142             return null;
1143         }
1144         int connectionState = sm.getConnectionState(device);
1145         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
1146             return null;
1147         }
1148         return sm.getCurrentAgFeaturesBundle();
1149     }
1150 
getCurrentAgFeatures(BluetoothDevice device)1151     public Set<Integer> getCurrentAgFeatures(BluetoothDevice device) {
1152         HeadsetClientStateMachine sm = getStateMachine(device);
1153         if (sm == null) {
1154             Log.e(TAG, "SM does not exist for device " + device);
1155             return null;
1156         }
1157         int connectionState = sm.getConnectionState(device);
1158         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
1159             return null;
1160         }
1161         return sm.getCurrentAgFeatures();
1162     }
1163 
1164     // Handle messages from native (JNI) to java
messageFromNative(StackEvent stackEvent)1165     public void messageFromNative(StackEvent stackEvent) {
1166         Objects.requireNonNull(
1167                 stackEvent.device, "Device should never be null, event: " + stackEvent);
1168 
1169         HeadsetClientStateMachine sm =
1170                 getStateMachine(stackEvent.device, isConnectionEvent(stackEvent));
1171         if (sm == null) {
1172             throw new IllegalStateException(
1173                     "State machine not found for stack event: " + stackEvent);
1174         }
1175         sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);
1176     }
1177 
isConnectionEvent(StackEvent stackEvent)1178     private boolean isConnectionEvent(StackEvent stackEvent) {
1179         if (stackEvent.type == StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
1180             if ((stackEvent.valueInt == HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING)
1181                     || (stackEvent.valueInt
1182                             == HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED)) {
1183                 return true;
1184             }
1185         }
1186         return false;
1187     }
1188 
getStateMachine(BluetoothDevice device)1189     private HeadsetClientStateMachine getStateMachine(BluetoothDevice device) {
1190         return getStateMachine(device, false);
1191     }
1192 
getStateMachine( BluetoothDevice device, boolean isConnectionEvent)1193     private HeadsetClientStateMachine getStateMachine(
1194             BluetoothDevice device, boolean isConnectionEvent) {
1195         if (device == null) {
1196             Log.e(TAG, "getStateMachine failed: Device cannot be null");
1197             return null;
1198         }
1199 
1200         HeadsetClientStateMachine sm;
1201         synchronized (mStateMachineMap) {
1202             sm = mStateMachineMap.get(device);
1203         }
1204 
1205         if (sm != null) {
1206             Log.d(TAG, "Found SM for device " + device);
1207         } else if (isConnectionEvent) {
1208             // The only time a new state machine should be created when none was found is for
1209             // connection events.
1210             sm = allocateStateMachine(device);
1211             if (sm == null) {
1212                 Log.e(TAG, "SM could not be allocated for device " + device);
1213             }
1214         }
1215         return sm;
1216     }
1217 
allocateStateMachine(BluetoothDevice device)1218     private HeadsetClientStateMachine allocateStateMachine(BluetoothDevice device) {
1219         if (device == null) {
1220             Log.e(TAG, "allocateStateMachine failed: Device cannot be null");
1221             return null;
1222         }
1223 
1224         if (getHeadsetClientService() == null) {
1225             // Preconditions: {@code setHeadsetClientService(this)} is the last thing {@code start}
1226             // does, and {@code setHeadsetClientService(null)} is (one of) the first thing
1227             // {@code stop does}.
1228             Log.e(
1229                     TAG,
1230                     "Cannot allocate SM if service has begun stopping or has not completed"
1231                             + " startup.");
1232             return null;
1233         }
1234 
1235         synchronized (mStateMachineMap) {
1236             HeadsetClientStateMachine sm = mStateMachineMap.get(device);
1237             if (sm != null) {
1238                 Log.d(TAG, "allocateStateMachine: SM already exists for device " + device);
1239                 return sm;
1240             }
1241 
1242             // There is a possibility of a DOS attack if someone populates here with a lot of fake
1243             // BluetoothAddresses. If it so happens instead of blowing up we can at least put a
1244             // limit on how long the attack would survive
1245             if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) {
1246                 Log.e(
1247                         TAG,
1248                         "Max state machines reached, possible DOS attack "
1249                                 + MAX_STATE_MACHINES_POSSIBLE);
1250                 return null;
1251             }
1252 
1253             // Allocate a new SM
1254             Log.d(TAG, "Creating a new state machine");
1255             sm = mSmFactory.make(this, mSmThread, mNativeInterface);
1256             mStateMachineMap.put(device, sm);
1257             return sm;
1258         }
1259     }
1260 
1261     // Check if any of the state machines have routed the SCO audio stream.
isScoRouted()1262     boolean isScoRouted() {
1263         synchronized (mStateMachineMap) {
1264             for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry :
1265                     mStateMachineMap.entrySet()) {
1266                 if (entry.getValue() != null) {
1267                     int audioState = entry.getValue().getAudioState(entry.getKey());
1268                     if (audioState == HeadsetClientHalConstants.AUDIO_STATE_CONNECTED) {
1269                         Log.d(
1270                                 TAG,
1271                                 "Device "
1272                                         + entry.getKey()
1273                                         + " audio state "
1274                                         + audioState
1275                                         + " Connected");
1276                         return true;
1277                     }
1278                 }
1279             }
1280         }
1281         return false;
1282     }
1283 
handleBatteryLevelChanged(BluetoothDevice device, int batteryLevel)1284     void handleBatteryLevelChanged(BluetoothDevice device, int batteryLevel) {
1285         AdapterService.getAdapterService()
1286                 .getRemoteDevices()
1287                 .handleAgBatteryLevelChanged(device, batteryLevel);
1288     }
1289 
1290     @Override
dump(StringBuilder sb)1291     public void dump(StringBuilder sb) {
1292         super.dump(sb);
1293         synchronized (mStateMachineMap) {
1294             for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
1295                 if (sm != null) {
1296                     sm.dump(sb);
1297                 }
1298             }
1299 
1300             sb.append("\n");
1301             HfpClientConnectionService.dump(sb);
1302         }
1303     }
1304 
1305     // For testing
getStateMachineMap()1306     protected Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() {
1307         synchronized (mStateMachineMap) {
1308             return mStateMachineMap;
1309         }
1310     }
1311 
setSMFactory(HeadsetClientStateMachineFactory factory)1312     protected void setSMFactory(HeadsetClientStateMachineFactory factory) {
1313         mSmFactory = factory;
1314     }
1315 
getAudioManager()1316     protected AudioManager getAudioManager() {
1317         return mAudioManager;
1318     }
1319 
updateBatteryLevel()1320     protected void updateBatteryLevel() {
1321         int batteryLevel = mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
1322         int batteryIndicatorID = 2;
1323 
1324         synchronized (mStateMachineMap) {
1325             for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
1326                 if (sm != null) {
1327                     sm.sendMessage(
1328                             HeadsetClientStateMachine.SEND_BIEV, batteryIndicatorID, batteryLevel);
1329                 }
1330             }
1331         }
1332     }
1333 }
1334