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