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