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