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