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.bluetooth.BluetoothDevice;
20 import android.bluetooth.BluetoothHeadsetClient;
21 import android.bluetooth.BluetoothHeadsetClientCall;
22 import android.bluetooth.BluetoothProfile;
23 import android.bluetooth.IBluetoothHeadsetClient;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.media.AudioManager;
29 import android.os.Bundle;
30 import android.os.HandlerThread;
31 import android.os.Message;
32 import android.util.Log;
33 
34 import com.android.bluetooth.Utils;
35 import com.android.bluetooth.btservice.AdapterService;
36 import com.android.bluetooth.btservice.ProfileService;
37 import com.android.bluetooth.btservice.storage.DatabaseManager;
38 import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService;
39 
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Objects;
46 import java.util.UUID;
47 
48 /**
49  * Provides Bluetooth Headset Client (HF Role) profile, as a service in the
50  * Bluetooth application.
51  *
52  * @hide
53  */
54 public class HeadsetClientService extends ProfileService {
55     private static final boolean DBG = false;
56     private static final String TAG = "HeadsetClientService";
57 
58     private HashMap<BluetoothDevice, HeadsetClientStateMachine> mStateMachineMap = new HashMap<>();
59     private static HeadsetClientService sHeadsetClientService;
60     private NativeInterface mNativeInterface = null;
61     private HandlerThread mSmThread = null;
62     private HeadsetClientStateMachineFactory mSmFactory = null;
63     private DatabaseManager mDatabaseManager;
64     private AudioManager mAudioManager = null;
65     // Maxinum number of devices we can try connecting to in one session
66     private static final int MAX_STATE_MACHINES_POSSIBLE = 100;
67 
68     public static final String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag";
69 
70     @Override
initBinder()71     public IProfileServiceBinder initBinder() {
72         return new BluetoothHeadsetClientBinder(this);
73     }
74 
75     @Override
start()76     protected synchronized boolean start() {
77         if (DBG) {
78             Log.d(TAG, "start()");
79         }
80         if (sHeadsetClientService != null) {
81             Log.w(TAG, "start(): start called without stop");
82             return false;
83         }
84 
85         mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(),
86                 "DatabaseManager cannot be null when HeadsetClientService starts");
87 
88         // Setup the JNI service
89         mNativeInterface = NativeInterface.getInstance();
90         mNativeInterface.initialize();
91 
92         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
93         if (mAudioManager == null) {
94             Log.e(TAG, "AudioManager service doesn't exist?");
95         } else {
96             // start AudioManager in a known state
97             mAudioManager.setParameters("hfp_enable=false");
98         }
99 
100         mSmFactory = new HeadsetClientStateMachineFactory();
101         mStateMachineMap.clear();
102 
103         IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
104         registerReceiver(mBroadcastReceiver, filter);
105 
106         // Start the HfpClientConnectionService to create connection with telecom when HFP
107         // connection is available.
108         Intent startIntent = new Intent(this, HfpClientConnectionService.class);
109         startService(startIntent);
110 
111         // Create the thread on which all State Machines will run
112         mSmThread = new HandlerThread("HeadsetClient.SM");
113         mSmThread.start();
114 
115         setHeadsetClientService(this);
116         return true;
117     }
118 
119     @Override
stop()120     protected synchronized boolean stop() {
121         if (sHeadsetClientService == null) {
122             Log.w(TAG, "stop() called without start()");
123             return false;
124         }
125 
126         // Stop the HfpClientConnectionService.
127         Intent stopIntent = new Intent(this, HfpClientConnectionService.class);
128         sHeadsetClientService.stopService(stopIntent);
129 
130         setHeadsetClientService(null);
131 
132         unregisterReceiver(mBroadcastReceiver);
133 
134         for (Iterator<Map.Entry<BluetoothDevice, HeadsetClientStateMachine>> it =
135                 mStateMachineMap.entrySet().iterator(); it.hasNext(); ) {
136             HeadsetClientStateMachine sm =
137                     mStateMachineMap.get((BluetoothDevice) it.next().getKey());
138             sm.doQuit();
139             it.remove();
140         }
141 
142         // Stop the handler thread
143         mSmThread.quit();
144         mSmThread = null;
145 
146         mNativeInterface.cleanup();
147         mNativeInterface = null;
148 
149         return true;
150     }
151 
152     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
153         @Override
154         public void onReceive(Context context, Intent intent) {
155             String action = intent.getAction();
156 
157             // We handle the volume changes for Voice calls here since HFP audio volume control does
158             // not go through audio manager (audio mixer). see
159             // ({@link HeadsetClientStateMachine#SET_SPEAKER_VOLUME} in
160             // {@link HeadsetClientStateMachine} for details.
161             if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
162                 if (DBG) {
163                     Log.d(TAG, "Volume changed for stream: " + intent.getExtra(
164                             AudioManager.EXTRA_VOLUME_STREAM_TYPE));
165                 }
166                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
167                 if (streamType == AudioManager.STREAM_VOICE_CALL) {
168                     int streamValue =
169                             intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
170                     int hfVol = HeadsetClientStateMachine.amToHfVol(streamValue);
171                     if (DBG) {
172                         Log.d(TAG,
173                                 "Setting volume to audio manager: " + streamValue + " hands free: "
174                                         + hfVol);
175                     }
176                     mAudioManager.setParameters("hfp_volume=" + hfVol);
177                     synchronized (this) {
178                         for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
179                             if (sm != null) {
180                                 sm.sendMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME,
181                                         streamValue);
182                             }
183                         }
184                     }
185                 }
186             }
187         }
188     };
189 
190     /**
191      * Handlers for incoming service calls
192      */
193     private static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
194             implements IProfileServiceBinder {
195         private HeadsetClientService mService;
196 
BluetoothHeadsetClientBinder(HeadsetClientService svc)197         BluetoothHeadsetClientBinder(HeadsetClientService svc) {
198             mService = svc;
199         }
200 
201         @Override
cleanup()202         public void cleanup() {
203             mService = null;
204         }
205 
getService()206         private HeadsetClientService getService() {
207             if (!Utils.checkCaller()) {
208                 Log.w(TAG, "HeadsetClient call not allowed for non-active user");
209                 return null;
210             }
211 
212             if (mService != null && mService.isAvailable()) {
213                 return mService;
214             }
215 
216             Log.e(TAG, "HeadsetClientService is not available.");
217             return null;
218         }
219 
220         @Override
connect(BluetoothDevice device)221         public boolean connect(BluetoothDevice device) {
222             HeadsetClientService service = getService();
223             if (service == null) {
224                 return false;
225             }
226             return service.connect(device);
227         }
228 
229         @Override
disconnect(BluetoothDevice device)230         public boolean disconnect(BluetoothDevice device) {
231             HeadsetClientService service = getService();
232             if (service == null) {
233                 return false;
234             }
235             return service.disconnect(device);
236         }
237 
238         @Override
getConnectedDevices()239         public List<BluetoothDevice> getConnectedDevices() {
240             HeadsetClientService service = getService();
241             if (service == null) {
242                 return new ArrayList<BluetoothDevice>(0);
243             }
244             return service.getConnectedDevices();
245         }
246 
247         @Override
getDevicesMatchingConnectionStates(int[] states)248         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
249             HeadsetClientService service = getService();
250             if (service == null) {
251                 return new ArrayList<BluetoothDevice>(0);
252             }
253             return service.getDevicesMatchingConnectionStates(states);
254         }
255 
256         @Override
getConnectionState(BluetoothDevice device)257         public int getConnectionState(BluetoothDevice device) {
258             HeadsetClientService service = getService();
259             if (service == null) {
260                 return BluetoothProfile.STATE_DISCONNECTED;
261             }
262             return service.getConnectionState(device);
263         }
264 
265         @Override
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)266         public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
267             HeadsetClientService service = getService();
268             if (service == null) {
269                 return false;
270             }
271             return service.setConnectionPolicy(device, connectionPolicy);
272         }
273 
274         @Override
getConnectionPolicy(BluetoothDevice device)275         public int getConnectionPolicy(BluetoothDevice device) {
276             HeadsetClientService service = getService();
277             if (service == null) {
278                 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
279             }
280             return service.getConnectionPolicy(device);
281         }
282 
283         @Override
startVoiceRecognition(BluetoothDevice device)284         public boolean startVoiceRecognition(BluetoothDevice device) {
285             HeadsetClientService service = getService();
286             if (service == null) {
287                 return false;
288             }
289             return service.startVoiceRecognition(device);
290         }
291 
292         @Override
stopVoiceRecognition(BluetoothDevice device)293         public boolean stopVoiceRecognition(BluetoothDevice device) {
294             HeadsetClientService service = getService();
295             if (service == null) {
296                 return false;
297             }
298             return service.stopVoiceRecognition(device);
299         }
300 
301         @Override
getAudioState(BluetoothDevice device)302         public int getAudioState(BluetoothDevice device) {
303             HeadsetClientService service = getService();
304             if (service == null) {
305                 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
306             }
307             return service.getAudioState(device);
308         }
309 
310         @Override
setAudioRouteAllowed(BluetoothDevice device, boolean allowed)311         public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
312             Log.e(TAG, "setAudioRouteAllowed API not supported");
313         }
314 
315         @Override
getAudioRouteAllowed(BluetoothDevice device)316         public boolean getAudioRouteAllowed(BluetoothDevice device) {
317             Log.e(TAG, "getAudioRouteAllowed API not supported");
318             return false;
319         }
320 
321         @Override
connectAudio(BluetoothDevice device)322         public boolean connectAudio(BluetoothDevice device) {
323             HeadsetClientService service = getService();
324             if (service == null) {
325                 return false;
326             }
327             return service.connectAudio(device);
328         }
329 
330         @Override
disconnectAudio(BluetoothDevice device)331         public boolean disconnectAudio(BluetoothDevice device) {
332             HeadsetClientService service = getService();
333             if (service == null) {
334                 return false;
335             }
336             return service.disconnectAudio(device);
337         }
338 
339         @Override
acceptCall(BluetoothDevice device, int flag)340         public boolean acceptCall(BluetoothDevice device, int flag) {
341             HeadsetClientService service = getService();
342             if (service == null) {
343                 return false;
344             }
345             return service.acceptCall(device, flag);
346         }
347 
348         @Override
rejectCall(BluetoothDevice device)349         public boolean rejectCall(BluetoothDevice device) {
350             HeadsetClientService service = getService();
351             if (service == null) {
352                 return false;
353             }
354             return service.rejectCall(device);
355         }
356 
357         @Override
holdCall(BluetoothDevice device)358         public boolean holdCall(BluetoothDevice device) {
359             HeadsetClientService service = getService();
360             if (service == null) {
361                 return false;
362             }
363             return service.holdCall(device);
364         }
365 
366         @Override
terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call)367         public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
368             HeadsetClientService service = getService();
369             if (service == null) {
370                 Log.w(TAG, "service is null");
371                 return false;
372             }
373             return service.terminateCall(device, call != null ? call.getUUID() : null);
374         }
375 
376         @Override
explicitCallTransfer(BluetoothDevice device)377         public boolean explicitCallTransfer(BluetoothDevice device) {
378             HeadsetClientService service = getService();
379             if (service == null) {
380                 return false;
381             }
382             return service.explicitCallTransfer(device);
383         }
384 
385         @Override
enterPrivateMode(BluetoothDevice device, int index)386         public boolean enterPrivateMode(BluetoothDevice device, int index) {
387             HeadsetClientService service = getService();
388             if (service == null) {
389                 return false;
390             }
391             return service.enterPrivateMode(device, index);
392         }
393 
394         @Override
dial(BluetoothDevice device, String number)395         public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
396             HeadsetClientService service = getService();
397             if (service == null) {
398                 return null;
399             }
400             return service.dial(device, number);
401         }
402 
403         @Override
getCurrentCalls(BluetoothDevice device)404         public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
405             HeadsetClientService service = getService();
406             if (service == null) {
407                 return new ArrayList<BluetoothHeadsetClientCall>();
408             }
409             return service.getCurrentCalls(device);
410         }
411 
412         @Override
sendDTMF(BluetoothDevice device, byte code)413         public boolean sendDTMF(BluetoothDevice device, byte code) {
414             HeadsetClientService service = getService();
415             if (service == null) {
416                 return false;
417             }
418             return service.sendDTMF(device, code);
419         }
420 
421         @Override
getLastVoiceTagNumber(BluetoothDevice device)422         public boolean getLastVoiceTagNumber(BluetoothDevice device) {
423             HeadsetClientService service = getService();
424             if (service == null) {
425                 return false;
426             }
427             return service.getLastVoiceTagNumber(device);
428         }
429 
430         @Override
getCurrentAgEvents(BluetoothDevice device)431         public Bundle getCurrentAgEvents(BluetoothDevice device) {
432             HeadsetClientService service = getService();
433             if (service == null) {
434                 return null;
435             }
436             return service.getCurrentAgEvents(device);
437         }
438 
439         @Override
sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand)440         public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) {
441             HeadsetClientService service = getService();
442             if (service == null) {
443                 return false;
444             }
445             return service.sendVendorAtCommand(device, vendorId, atCommand);
446         }
447 
448         @Override
getCurrentAgFeatures(BluetoothDevice device)449         public Bundle getCurrentAgFeatures(BluetoothDevice device) {
450             HeadsetClientService service = getService();
451             if (service == null) {
452                 return null;
453             }
454             return service.getCurrentAgFeatures(device);
455         }
456     }
457 
458     ;
459 
460     // API methods
getHeadsetClientService()461     public static synchronized HeadsetClientService getHeadsetClientService() {
462         if (sHeadsetClientService == null) {
463             Log.w(TAG, "getHeadsetClientService(): service is null");
464             return null;
465         }
466         if (!sHeadsetClientService.isAvailable()) {
467             Log.w(TAG, "getHeadsetClientService(): service is not available ");
468             return null;
469         }
470         return sHeadsetClientService;
471     }
472 
setHeadsetClientService(HeadsetClientService instance)473     private static synchronized void setHeadsetClientService(HeadsetClientService instance) {
474         if (DBG) {
475             Log.d(TAG, "setHeadsetClientService(): set to: " + instance);
476         }
477         sHeadsetClientService = instance;
478     }
479 
connect(BluetoothDevice device)480     public boolean connect(BluetoothDevice device) {
481         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
482         if (DBG) {
483             Log.d(TAG, "connect " + device);
484         }
485         HeadsetClientStateMachine sm = getStateMachine(device);
486         if (sm == null) {
487             Log.e(TAG, "Cannot allocate SM for device " + device);
488             return false;
489         }
490 
491         if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
492             Log.w(TAG, "Connection not allowed: <" + device.getAddress()
493                     + "> is CONNECTION_POLICY_FORBIDDEN");
494             return false;
495         }
496 
497         sm.sendMessage(HeadsetClientStateMachine.CONNECT, device);
498         return true;
499     }
500 
501     /**
502      * Disconnects hfp client for the remote bluetooth device
503      *
504      * @param device is the device with which we are attempting to disconnect the profile
505      * @return true if hfp client profile successfully disconnected, false otherwise
506      */
disconnect(BluetoothDevice device)507     public boolean disconnect(BluetoothDevice device) {
508         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
509         HeadsetClientStateMachine sm = getStateMachine(device);
510         if (sm == null) {
511             Log.e(TAG, "Cannot allocate SM for device " + device);
512             return false;
513         }
514 
515         int connectionState = sm.getConnectionState(device);
516         if (connectionState != BluetoothProfile.STATE_CONNECTED
517                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
518             return false;
519         }
520 
521         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
522         return true;
523     }
524 
getConnectedDevices()525     public synchronized List<BluetoothDevice> getConnectedDevices() {
526         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
527 
528         ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>();
529         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
530             HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
531             if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) {
532                 connectedDevices.add(bd);
533             }
534         }
535         return connectedDevices;
536     }
537 
getDevicesMatchingConnectionStates(int[] states)538     private synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
539         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
540         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
541         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
542             for (int state : states) {
543                 HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
544                 if (sm != null && sm.getConnectionState(bd) == state) {
545                     devices.add(bd);
546                 }
547             }
548         }
549         return devices;
550     }
551 
552     /**
553      * Get the current connection state of the profile
554      *
555      * @param device is the remote bluetooth device
556      * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected,
557      * {@link BluetoothProfile#STATE_CONNECTING} if this profile is being connected,
558      * {@link BluetoothProfile#STATE_CONNECTED} if this profile is connected, or
559      * {@link BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected
560      */
getConnectionState(BluetoothDevice device)561     public synchronized int getConnectionState(BluetoothDevice device) {
562         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
563         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
564         if (sm != null) {
565             return sm.getConnectionState(device);
566         }
567         return BluetoothProfile.STATE_DISCONNECTED;
568     }
569 
570     /**
571      * Set connection policy of the profile and connects it if connectionPolicy is
572      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is
573      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
574      *
575      * <p> The device should already be paired.
576      * Connection policy can be one of:
577      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
578      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
579      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
580      *
581      * @param device Paired bluetooth device
582      * @param connectionPolicy is the connection policy to set to for this profile
583      * @return true if connectionPolicy is set, false on error
584      */
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)585     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
586         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
587         if (DBG) {
588             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
589         }
590 
591         if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.HEADSET_CLIENT,
592                   connectionPolicy)) {
593             return false;
594         }
595         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
596             connect(device);
597         } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
598             disconnect(device);
599         }
600         return true;
601     }
602 
603     /**
604      * Get the connection policy of the profile.
605      *
606      * <p> The connection policy can be any of:
607      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
608      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
609      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
610      *
611      * @param device Bluetooth device
612      * @return connection policy of the device
613      * @hide
614      */
getConnectionPolicy(BluetoothDevice device)615     public int getConnectionPolicy(BluetoothDevice device) {
616         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
617         return mDatabaseManager
618                 .getProfileConnectionPolicy(device, BluetoothProfile.HEADSET_CLIENT);
619     }
620 
startVoiceRecognition(BluetoothDevice device)621     boolean startVoiceRecognition(BluetoothDevice device) {
622         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
623         HeadsetClientStateMachine sm = getStateMachine(device);
624         if (sm == null) {
625             Log.e(TAG, "Cannot allocate SM for device " + device);
626             return false;
627         }
628         int connectionState = sm.getConnectionState(device);
629         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
630             return false;
631         }
632         sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START);
633         return true;
634     }
635 
stopVoiceRecognition(BluetoothDevice device)636     boolean stopVoiceRecognition(BluetoothDevice device) {
637         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
638         HeadsetClientStateMachine sm = getStateMachine(device);
639         if (sm == null) {
640             Log.e(TAG, "Cannot allocate SM for device " + device);
641             return false;
642         }
643         int connectionState = sm.getConnectionState(device);
644         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
645             return false;
646         }
647         sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_STOP);
648         return true;
649     }
650 
getAudioState(BluetoothDevice device)651     int getAudioState(BluetoothDevice device) {
652         HeadsetClientStateMachine sm = getStateMachine(device);
653         if (sm == null) {
654             Log.e(TAG, "Cannot allocate SM for device " + device);
655             return -1;
656         }
657 
658         return sm.getAudioState(device);
659     }
660 
connectAudio(BluetoothDevice device)661     boolean connectAudio(BluetoothDevice device) {
662         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
663         HeadsetClientStateMachine sm = getStateMachine(device);
664         if (sm == null) {
665             Log.e(TAG, "Cannot allocate SM for device " + device);
666             return false;
667         }
668 
669         if (!sm.isConnected()) {
670             return false;
671         }
672         if (sm.isAudioOn()) {
673             return false;
674         }
675         sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
676         return true;
677     }
678 
disconnectAudio(BluetoothDevice device)679     boolean disconnectAudio(BluetoothDevice device) {
680         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
681         HeadsetClientStateMachine sm = getStateMachine(device);
682         if (sm == null) {
683             Log.e(TAG, "Cannot allocate SM for device " + device);
684             return false;
685         }
686 
687         if (!sm.isAudioOn()) {
688             return false;
689         }
690         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
691         return true;
692     }
693 
holdCall(BluetoothDevice device)694     boolean holdCall(BluetoothDevice device) {
695         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
696         HeadsetClientStateMachine sm = getStateMachine(device);
697         if (sm == null) {
698             Log.e(TAG, "Cannot allocate SM for device " + device);
699             return false;
700         }
701 
702         int connectionState = sm.getConnectionState(device);
703         if (connectionState != BluetoothProfile.STATE_CONNECTED
704                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
705             return false;
706         }
707         Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
708         sm.sendMessage(msg);
709         return true;
710     }
711 
acceptCall(BluetoothDevice device, int flag)712     boolean acceptCall(BluetoothDevice device, int flag) {
713         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
714         /* Phonecalls from a single device are supported, hang up any calls on the other phone */
715         synchronized (this) {
716             for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap
717                     .entrySet()) {
718                 if (entry.getValue() == null || entry.getKey().equals(device)) {
719                     continue;
720                 }
721                 int connectionState = entry.getValue().getConnectionState(entry.getKey());
722                 if (DBG) {
723                     Log.d(TAG,
724                             "Accepting a call on device " + device + ". Possibly disconnecting on "
725                                     + entry.getValue());
726                 }
727                 if (connectionState == BluetoothProfile.STATE_CONNECTED) {
728                     entry.getValue()
729                             .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL)
730                             .sendToTarget();
731                 }
732             }
733         }
734         HeadsetClientStateMachine sm = getStateMachine(device);
735         if (sm == null) {
736             Log.e(TAG, "Cannot allocate SM for device " + device);
737             return false;
738         }
739 
740         int connectionState = sm.getConnectionState(device);
741         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
742             return false;
743         }
744         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
745         msg.arg1 = flag;
746         sm.sendMessage(msg);
747         return true;
748     }
749 
rejectCall(BluetoothDevice device)750     boolean rejectCall(BluetoothDevice device) {
751         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
752         HeadsetClientStateMachine sm = getStateMachine(device);
753         if (sm == null) {
754             Log.e(TAG, "Cannot allocate SM for device " + device);
755             return false;
756         }
757 
758         int connectionState = sm.getConnectionState(device);
759         if (connectionState != BluetoothProfile.STATE_CONNECTED
760                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
761             return false;
762         }
763 
764         Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
765         sm.sendMessage(msg);
766         return true;
767     }
768 
terminateCall(BluetoothDevice device, UUID uuid)769     boolean terminateCall(BluetoothDevice device, UUID uuid) {
770         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
771         HeadsetClientStateMachine sm = getStateMachine(device);
772         if (sm == null) {
773             Log.e(TAG, "Cannot allocate SM for device " + device);
774             return false;
775         }
776 
777         int connectionState = sm.getConnectionState(device);
778         if (connectionState != BluetoothProfile.STATE_CONNECTED
779                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
780             return false;
781         }
782 
783         Message msg = sm.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
784         msg.obj = uuid;
785         sm.sendMessage(msg);
786         return true;
787     }
788 
enterPrivateMode(BluetoothDevice device, int index)789     boolean enterPrivateMode(BluetoothDevice device, int index) {
790         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
791         HeadsetClientStateMachine sm = getStateMachine(device);
792         if (sm == null) {
793             Log.e(TAG, "Cannot allocate SM for device " + device);
794             return false;
795         }
796 
797         int connectionState = sm.getConnectionState(device);
798         if (connectionState != BluetoothProfile.STATE_CONNECTED
799                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
800             return false;
801         }
802 
803         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
804         msg.arg1 = index;
805         sm.sendMessage(msg);
806         return true;
807     }
808 
dial(BluetoothDevice device, String number)809     BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
810         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
811         HeadsetClientStateMachine sm = getStateMachine(device);
812         if (sm == null) {
813             Log.e(TAG, "Cannot allocate SM for device " + device);
814             return null;
815         }
816 
817         int connectionState = sm.getConnectionState(device);
818         if (connectionState != BluetoothProfile.STATE_CONNECTED
819                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
820             return null;
821         }
822 
823         BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(device,
824                 HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID,
825                 BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false  /* multiparty */,
826                 true  /* outgoing */, sm.getInBandRing());
827         Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
828         msg.obj = call;
829         sm.sendMessage(msg);
830         return call;
831     }
832 
sendDTMF(BluetoothDevice device, byte code)833     public boolean sendDTMF(BluetoothDevice device, byte code) {
834         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
835         HeadsetClientStateMachine sm = getStateMachine(device);
836         if (sm == null) {
837             Log.e(TAG, "Cannot allocate SM for device " + device);
838             return false;
839         }
840 
841         int connectionState = sm.getConnectionState(device);
842         if (connectionState != BluetoothProfile.STATE_CONNECTED
843                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
844             return false;
845         }
846         Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
847         msg.arg1 = code;
848         sm.sendMessage(msg);
849         return true;
850     }
851 
getLastVoiceTagNumber(BluetoothDevice device)852     public boolean getLastVoiceTagNumber(BluetoothDevice device) {
853         return false;
854     }
855 
getCurrentCalls(BluetoothDevice device)856     public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
857         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
858         HeadsetClientStateMachine sm = getStateMachine(device);
859         if (sm == null) {
860             Log.e(TAG, "Cannot allocate SM for device " + device);
861             return null;
862         }
863 
864         int connectionState = sm.getConnectionState(device);
865         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
866             return null;
867         }
868         return sm.getCurrentCalls();
869     }
870 
explicitCallTransfer(BluetoothDevice device)871     public boolean explicitCallTransfer(BluetoothDevice device) {
872         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
873         HeadsetClientStateMachine sm = getStateMachine(device);
874         if (sm == null) {
875             Log.e(TAG, "Cannot allocate SM for device " + device);
876             return false;
877         }
878 
879         int connectionState = sm.getConnectionState(device);
880         if (connectionState != BluetoothProfile.STATE_CONNECTED
881                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
882             return false;
883         }
884         Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
885         sm.sendMessage(msg);
886         return true;
887     }
888 
889     /** Send vendor AT command. */
sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand)890     public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) {
891         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
892         HeadsetClientStateMachine sm = getStateMachine(device);
893         if (sm == null) {
894             Log.e(TAG, "Cannot allocate SM for device " + device);
895             return false;
896         }
897 
898         int connectionState = sm.getConnectionState(device);
899         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
900             return false;
901         }
902 
903         Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND,
904                                        vendorId, 0, atCommand);
905         sm.sendMessage(msg);
906         return true;
907     }
908 
getCurrentAgEvents(BluetoothDevice device)909     public Bundle getCurrentAgEvents(BluetoothDevice device) {
910         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
911         HeadsetClientStateMachine sm = getStateMachine(device);
912         if (sm == null) {
913             Log.e(TAG, "Cannot allocate SM for device " + device);
914             return null;
915         }
916 
917         int connectionState = sm.getConnectionState(device);
918         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
919             return null;
920         }
921         return sm.getCurrentAgEvents();
922     }
923 
getCurrentAgFeatures(BluetoothDevice device)924     public Bundle getCurrentAgFeatures(BluetoothDevice device) {
925         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
926         HeadsetClientStateMachine sm = getStateMachine(device);
927         if (sm == null) {
928             Log.e(TAG, "Cannot allocate SM for device " + device);
929             return null;
930         }
931         int connectionState = sm.getConnectionState(device);
932         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
933             return null;
934         }
935         return sm.getCurrentAgFeatures();
936     }
937 
938     // Handle messages from native (JNI) to java
messageFromNative(StackEvent stackEvent)939     public void messageFromNative(StackEvent stackEvent) {
940         HeadsetClientStateMachine sm = getStateMachine(stackEvent.device);
941         if (sm == null) {
942             Log.w(TAG, "No SM found for event " + stackEvent);
943         }
944 
945         sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);
946     }
947 
948     // State machine management
getStateMachine(BluetoothDevice device)949     private synchronized HeadsetClientStateMachine getStateMachine(BluetoothDevice device) {
950         if (device == null) {
951             Log.e(TAG, "getStateMachine failed: Device cannot be null");
952             return null;
953         }
954 
955         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
956         if (sm != null) {
957             if (DBG) {
958                 Log.d(TAG, "Found SM for device " + device);
959             }
960             return sm;
961         }
962 
963         // There is a possibility of a DOS attack if someone populates here with a lot of fake
964         // BluetoothAddresses. If it so happens instead of blowing up we can atleast put a limit on
965         // how long the attack would survive
966         if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) {
967             Log.e(TAG, "Max state machines reached, possible DOS attack "
968                     + MAX_STATE_MACHINES_POSSIBLE);
969             return null;
970         }
971 
972         // Allocate a new SM
973         Log.d(TAG, "Creating a new state machine");
974         sm = mSmFactory.make(this, mSmThread, mNativeInterface);
975         mStateMachineMap.put(device, sm);
976         return sm;
977     }
978 
979     // Check if any of the state machines have routed the SCO audio stream.
isScoRouted()980     synchronized boolean isScoRouted() {
981         for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap
982                 .entrySet()) {
983             if (entry.getValue() != null) {
984                 int audioState = entry.getValue().getAudioState(entry.getKey());
985                 if (audioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) {
986                     if (DBG) {
987                         Log.d(TAG, "Device " + entry.getKey() + " audio state " + audioState
988                                 + " Connected");
989                     }
990                     return true;
991                 }
992             }
993         }
994         return false;
995     }
996 
997     @Override
dump(StringBuilder sb)998     public synchronized void dump(StringBuilder sb) {
999         super.dump(sb);
1000         for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
1001             if (sm != null) {
1002                 sm.dump(sb);
1003             }
1004         }
1005     }
1006 
1007     // For testing
getStateMachineMap()1008     protected synchronized Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() {
1009         return mStateMachineMap;
1010     }
1011 
setSMFactory(HeadsetClientStateMachineFactory factory)1012     protected void setSMFactory(HeadsetClientStateMachineFactory factory) {
1013         mSmFactory = factory;
1014     }
1015 
getAudioManager()1016     protected AudioManager getAudioManager() {
1017         return mAudioManager;
1018     }
1019 }
1020