• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.hfpclient;
18 
19 import android.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                 if (DBG) {
166                     Log.d(TAG,
167                             "Volume changed for stream: "
168                                     + intent.getExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE));
169                 }
170                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
171                 if (streamType == AudioManager.STREAM_VOICE_CALL) {
172                     int streamValue = intent
173                             .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
174                     int hfVol = HeadsetClientStateMachine.amToHfVol(streamValue);
175                     if (DBG) {
176                         Log.d(TAG,
177                                 "Setting volume to audio manager: " + streamValue
178                                         + " hands free: " + hfVol);
179                     }
180                     mAudioManager.setParameters("hfp_volume=" + hfVol);
181                     synchronized (this) {
182                         for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
183                             if (sm != null) {
184                                 sm.sendMessage(
185                                         HeadsetClientStateMachine.SET_SPEAKER_VOLUME, streamValue);
186                             }
187                         }
188                     }
189                 }
190             }
191         }
192     };
193 
194     /**
195      * Handlers for incoming service calls
196      */
197     private static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
198             implements IProfileServiceBinder {
199         private HeadsetClientService mService;
200 
BluetoothHeadsetClientBinder(HeadsetClientService svc)201         public BluetoothHeadsetClientBinder(HeadsetClientService svc) {
202             mService = svc;
203         }
204 
205         @Override
cleanup()206         public boolean cleanup() {
207             mService = null;
208             return true;
209         }
210 
getService()211         private HeadsetClientService getService() {
212             if (!Utils.checkCaller()) {
213                 Log.w(TAG, "HeadsetClient call not allowed for non-active user");
214                 return null;
215             }
216 
217             if (mService != null && mService.isAvailable()) {
218                 return mService;
219             }
220 
221             Log.e(TAG, "HeadsetClientService is not available.");
222             return null;
223         }
224 
225         @Override
connect(BluetoothDevice device)226         public boolean connect(BluetoothDevice device) {
227             HeadsetClientService service = getService();
228             if (service == null) {
229                 return false;
230             }
231             return service.connect(device);
232         }
233 
234         @Override
disconnect(BluetoothDevice device)235         public boolean disconnect(BluetoothDevice device) {
236             HeadsetClientService service = getService();
237             if (service == null) {
238                 return false;
239             }
240             return service.disconnect(device);
241         }
242 
243         @Override
getConnectedDevices()244         public List<BluetoothDevice> getConnectedDevices() {
245             HeadsetClientService service = getService();
246             if (service == null) {
247                 return new ArrayList<BluetoothDevice>(0);
248             }
249             return service.getConnectedDevices();
250         }
251 
252         @Override
getDevicesMatchingConnectionStates(int[] states)253         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
254             HeadsetClientService service = getService();
255             if (service == null) {
256                 return new ArrayList<BluetoothDevice>(0);
257             }
258             return service.getDevicesMatchingConnectionStates(states);
259         }
260 
261         @Override
getConnectionState(BluetoothDevice device)262         public int getConnectionState(BluetoothDevice device) {
263             HeadsetClientService service = getService();
264             if (service == null) {
265                 return BluetoothProfile.STATE_DISCONNECTED;
266             }
267             return service.getConnectionState(device);
268         }
269 
270         @Override
setPriority(BluetoothDevice device, int priority)271         public boolean setPriority(BluetoothDevice device, int priority) {
272             HeadsetClientService service = getService();
273             if (service == null) {
274                 return false;
275             }
276             return service.setPriority(device, priority);
277         }
278 
279         @Override
getPriority(BluetoothDevice device)280         public int getPriority(BluetoothDevice device) {
281             HeadsetClientService service = getService();
282             if (service == null) {
283                 return BluetoothProfile.PRIORITY_UNDEFINED;
284             }
285             return service.getPriority(device);
286         }
287 
288         @Override
startVoiceRecognition(BluetoothDevice device)289         public boolean startVoiceRecognition(BluetoothDevice device) {
290             HeadsetClientService service = getService();
291             if (service == null) {
292                 return false;
293             }
294             return service.startVoiceRecognition(device);
295         }
296 
297         @Override
stopVoiceRecognition(BluetoothDevice device)298         public boolean stopVoiceRecognition(BluetoothDevice device) {
299             HeadsetClientService service = getService();
300             if (service == null) {
301                 return false;
302             }
303             return service.stopVoiceRecognition(device);
304         }
305 
306         @Override
getAudioState(BluetoothDevice device)307         public int getAudioState(BluetoothDevice device) {
308             HeadsetClientService service = getService();
309             if (service == null) {
310                 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
311             }
312             return service.getAudioState(device);
313         }
314 
315         @Override
setAudioRouteAllowed(BluetoothDevice device, boolean allowed)316         public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
317             Log.e(TAG, "setAudioRouteAllowed API not supported");
318         }
319 
320         @Override
getAudioRouteAllowed(BluetoothDevice device)321         public boolean getAudioRouteAllowed(BluetoothDevice device) {
322             Log.e(TAG, "getAudioRouteAllowed API not supported");
323             return false;
324         }
325 
326         @Override
connectAudio(BluetoothDevice device)327         public boolean connectAudio(BluetoothDevice device) {
328             HeadsetClientService service = getService();
329             if (service == null) {
330                 return false;
331             }
332             return service.connectAudio(device);
333         }
334 
335         @Override
disconnectAudio(BluetoothDevice device)336         public boolean disconnectAudio(BluetoothDevice device) {
337             HeadsetClientService service = getService();
338             if (service == null) {
339                 return false;
340             }
341             return service.disconnectAudio(device);
342         }
343 
344         @Override
acceptCall(BluetoothDevice device, int flag)345         public boolean acceptCall(BluetoothDevice device, int flag) {
346             HeadsetClientService service = getService();
347             if (service == null) {
348                 return false;
349             }
350             return service.acceptCall(device, flag);
351         }
352 
353         @Override
rejectCall(BluetoothDevice device)354         public boolean rejectCall(BluetoothDevice device) {
355             HeadsetClientService service = getService();
356             if (service == null) {
357                 return false;
358             }
359             return service.rejectCall(device);
360         }
361 
362         @Override
holdCall(BluetoothDevice device)363         public boolean holdCall(BluetoothDevice device) {
364             HeadsetClientService service = getService();
365             if (service == null) {
366                 return false;
367             }
368             return service.holdCall(device);
369         }
370 
371         @Override
terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call)372         public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
373             HeadsetClientService service = getService();
374             if (service == null) {
375                 Log.w(TAG, "service is null");
376                 return false;
377             }
378             return service.terminateCall(device, call != null ? call.getUUID() : null);
379         }
380 
381         @Override
explicitCallTransfer(BluetoothDevice device)382         public boolean explicitCallTransfer(BluetoothDevice device) {
383             HeadsetClientService service = getService();
384             if (service == null) {
385                 return false;
386             }
387             return service.explicitCallTransfer(device);
388         }
389 
390         @Override
enterPrivateMode(BluetoothDevice device, int index)391         public boolean enterPrivateMode(BluetoothDevice device, int index) {
392             HeadsetClientService service = getService();
393             if (service == null) {
394                 return false;
395             }
396             return service.enterPrivateMode(device, index);
397         }
398 
399         @Override
dial(BluetoothDevice device, String number)400         public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
401             HeadsetClientService service = getService();
402             if (service == null) {
403                 return null;
404             }
405             return service.dial(device, number);
406         }
407 
408         @Override
getCurrentCalls(BluetoothDevice device)409         public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
410             HeadsetClientService service = getService();
411             if (service == null) {
412                 return new ArrayList<BluetoothHeadsetClientCall>();
413             }
414             return service.getCurrentCalls(device);
415         }
416 
417         @Override
sendDTMF(BluetoothDevice device, byte code)418         public boolean sendDTMF(BluetoothDevice device, byte code) {
419             HeadsetClientService service = getService();
420             if (service == null) {
421                 return false;
422             }
423             return service.sendDTMF(device, code);
424         }
425 
426         @Override
getLastVoiceTagNumber(BluetoothDevice device)427         public boolean getLastVoiceTagNumber(BluetoothDevice device) {
428             HeadsetClientService service = getService();
429             if (service == null) {
430                 return false;
431             }
432             return service.getLastVoiceTagNumber(device);
433         }
434 
435         @Override
getCurrentAgEvents(BluetoothDevice device)436         public Bundle getCurrentAgEvents(BluetoothDevice device) {
437             HeadsetClientService service = getService();
438             if (service == null) {
439                 return null;
440             }
441             return service.getCurrentAgEvents(device);
442         }
443 
444         @Override
getCurrentAgFeatures(BluetoothDevice device)445         public Bundle getCurrentAgFeatures(BluetoothDevice device) {
446             HeadsetClientService service = getService();
447             if (service == null) {
448                 return null;
449             }
450             return service.getCurrentAgFeatures(device);
451         }
452     };
453 
454     // API methods
getHeadsetClientService()455     public static synchronized HeadsetClientService getHeadsetClientService() {
456         if (sHeadsetClientService != null && sHeadsetClientService.isAvailable()) {
457             if (DBG) {
458                 Log.d(TAG, "getHeadsetClientService(): returning " + sHeadsetClientService);
459             }
460             return sHeadsetClientService;
461         }
462         if (DBG) {
463             if (sHeadsetClientService == null) {
464                 Log.d(TAG, "getHeadsetClientService(): service is NULL");
465             } else if (!(sHeadsetClientService.isAvailable())) {
466                 Log.d(TAG, "getHeadsetClientService(): service is not available");
467             }
468         }
469         return null;
470     }
471 
setHeadsetClientService(HeadsetClientService instance)472     private static synchronized void setHeadsetClientService(HeadsetClientService instance) {
473         if (instance != null && instance.isAvailable()) {
474             if (DBG) {
475                 Log.d(TAG, "setHeadsetClientService(): set to: " + sHeadsetClientService);
476             }
477             sHeadsetClientService = instance;
478         } else {
479             if (DBG) {
480                 if (sHeadsetClientService == null) {
481                     Log.d(TAG, "setHeadsetClientService(): service not available");
482                 } else if (!sHeadsetClientService.isAvailable()) {
483                     Log.d(TAG, "setHeadsetClientService(): service is cleaning up");
484                 }
485             }
486         }
487     }
488 
clearHeadsetClientService()489     private static synchronized void clearHeadsetClientService() {
490         sHeadsetClientService = null;
491     }
492 
connect(BluetoothDevice device)493     public boolean connect(BluetoothDevice device) {
494         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
495                 "Need BLUETOOTH ADMIN permission");
496         if (DBG) {
497             Log.d(TAG, "connect " + device);
498         }
499         HeadsetClientStateMachine sm = getStateMachine(device);
500         if (sm == null) {
501             Log.e(TAG, "Cannot allocate SM for device " + device);
502             return false;
503         }
504 
505         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
506             Log.w(TAG, "Connection not allowed: <" + device.getAddress() + "> is PRIORITY_OFF");
507             return false;
508         }
509 
510         sm.sendMessage(HeadsetClientStateMachine.CONNECT, device);
511         return true;
512     }
513 
disconnect(BluetoothDevice device)514     boolean disconnect(BluetoothDevice device) {
515         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
516                 "Need BLUETOOTH ADMIN permission");
517         HeadsetClientStateMachine sm = getStateMachine(device);
518         if (sm == null) {
519             Log.e(TAG, "Cannot allocate SM for device " + device);
520             return false;
521         }
522 
523         int connectionState = sm.getConnectionState(device);
524         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
525                 connectionState != BluetoothProfile.STATE_CONNECTING) {
526             return false;
527         }
528 
529         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
530         return true;
531     }
532 
getConnectedDevices()533     public synchronized List<BluetoothDevice> getConnectedDevices() {
534         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
535 
536         ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>();
537         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
538             HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
539             if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) {
540                 connectedDevices.add(bd);
541             }
542         }
543         return connectedDevices;
544     }
545 
getDevicesMatchingConnectionStates(int[] states)546     private synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
547         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
548         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
549         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
550             for (int state : states) {
551                 HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
552                 if (sm != null && sm.getConnectionState(bd) == state) {
553                     devices.add(bd);
554                 }
555             }
556         }
557         return devices;
558     }
559 
getConnectionState(BluetoothDevice device)560     private synchronized int getConnectionState(BluetoothDevice device) {
561         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
562         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
563         if (sm != null) {
564             return sm.getConnectionState(device);
565         }
566         return BluetoothProfile.STATE_DISCONNECTED;
567     }
568 
setPriority(BluetoothDevice device, int priority)569     public boolean setPriority(BluetoothDevice device, int priority) {
570         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
571                 "Need BLUETOOTH_ADMIN permission");
572         Settings.Global.putInt(getContentResolver(),
573                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
574                 priority);
575         if (DBG) {
576             Log.d(TAG, "Saved priority " + device + " = " + priority);
577         }
578         return true;
579     }
580 
getPriority(BluetoothDevice device)581     public int getPriority(BluetoothDevice device) {
582         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
583                 "Need BLUETOOTH_ADMIN permission");
584         int priority = Settings.Global.getInt(getContentResolver(),
585                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
586                 BluetoothProfile.PRIORITY_UNDEFINED);
587         return priority;
588     }
589 
startVoiceRecognition(BluetoothDevice device)590     boolean startVoiceRecognition(BluetoothDevice device) {
591         Log.e(TAG, "startVoiceRecognition API not available");
592         return false;
593     }
594 
stopVoiceRecognition(BluetoothDevice device)595     boolean stopVoiceRecognition(BluetoothDevice device) {
596         Log.e(TAG, "stopVoiceRecognition API not available");
597         return false;
598     }
599 
getAudioState(BluetoothDevice device)600     int getAudioState(BluetoothDevice device) {
601         HeadsetClientStateMachine sm = getStateMachine(device);
602         if (sm == null) {
603             Log.e(TAG, "Cannot allocate SM for device " + device);
604             return -1;
605         }
606 
607         return sm.getAudioState(device);
608     }
609 
connectAudio(BluetoothDevice device)610     boolean connectAudio(BluetoothDevice device) {
611         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
612         HeadsetClientStateMachine sm = getStateMachine(device);
613         if (sm == null) {
614             Log.e(TAG, "Cannot allocate SM for device " + device);
615             return false;
616         }
617 
618         if (!sm.isConnected()) {
619             return false;
620         }
621         if (sm.isAudioOn()) {
622             return false;
623         }
624         sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
625         return true;
626     }
627 
disconnectAudio(BluetoothDevice device)628     boolean disconnectAudio(BluetoothDevice device) {
629         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
630         HeadsetClientStateMachine sm = getStateMachine(device);
631         if (sm == null) {
632             Log.e(TAG, "Cannot allocate SM for device " + device);
633             return false;
634         }
635 
636         if (!sm.isAudioOn()) {
637             return false;
638         }
639         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
640         return true;
641     }
642 
holdCall(BluetoothDevice device)643     boolean holdCall(BluetoothDevice device) {
644         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
645         HeadsetClientStateMachine sm = getStateMachine(device);
646         if (sm == null) {
647             Log.e(TAG, "Cannot allocate SM for device " + device);
648             return false;
649         }
650 
651         int connectionState = sm.getConnectionState(device);
652         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
653                 connectionState != BluetoothProfile.STATE_CONNECTING) {
654             return false;
655         }
656         Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
657         sm.sendMessage(msg);
658         return true;
659     }
660 
acceptCall(BluetoothDevice device, int flag)661     boolean acceptCall(BluetoothDevice device, int flag) {
662         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
663         /* Phonecalls from a single device are supported, hang up any calls on the other phone */
664         synchronized (this) {
665             for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry :
666                     mStateMachineMap.entrySet()) {
667                 if (entry.getValue() == null || entry.getKey().equals(device)) {
668                     continue;
669                 }
670                 int connectionState = entry.getValue().getConnectionState(entry.getKey());
671                 if (DBG) {
672                     Log.d(TAG, "Accepting a call on device " + device
673                                     + ". Possibly disconnecting on " + entry.getValue());
674                 }
675                 if (connectionState == BluetoothProfile.STATE_CONNECTED) {
676                     entry.getValue()
677                             .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL)
678                             .sendToTarget();
679                 }
680             }
681         }
682         HeadsetClientStateMachine sm = getStateMachine(device);
683         if (sm == null) {
684             Log.e(TAG, "Cannot allocate SM for device " + device);
685             return false;
686         }
687 
688         int connectionState = sm.getConnectionState(device);
689         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
690             return false;
691         }
692         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
693         msg.arg1 = flag;
694         sm.sendMessage(msg);
695         return true;
696     }
697 
rejectCall(BluetoothDevice device)698     boolean rejectCall(BluetoothDevice device) {
699         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
700         HeadsetClientStateMachine sm = getStateMachine(device);
701         if (sm == null) {
702             Log.e(TAG, "Cannot allocate SM for device " + device);
703             return false;
704         }
705 
706         int connectionState = sm.getConnectionState(device);
707         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
708                 connectionState != BluetoothProfile.STATE_CONNECTING) {
709             return false;
710         }
711 
712         Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
713         sm.sendMessage(msg);
714         return true;
715     }
716 
terminateCall(BluetoothDevice device, UUID uuid)717     boolean terminateCall(BluetoothDevice device, UUID uuid) {
718         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
719         HeadsetClientStateMachine sm = getStateMachine(device);
720         if (sm == null) {
721             Log.e(TAG, "Cannot allocate SM for device " + device);
722             return false;
723         }
724 
725         int connectionState = sm.getConnectionState(device);
726         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
727                 connectionState != BluetoothProfile.STATE_CONNECTING) {
728             return false;
729         }
730 
731         Message msg = sm.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
732         msg.obj = uuid;
733         sm.sendMessage(msg);
734         return true;
735     }
736 
enterPrivateMode(BluetoothDevice device, int index)737     boolean enterPrivateMode(BluetoothDevice device, int index) {
738         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
739         HeadsetClientStateMachine sm = getStateMachine(device);
740         if (sm == null) {
741             Log.e(TAG, "Cannot allocate SM for device " + device);
742             return false;
743         }
744 
745         int connectionState = sm.getConnectionState(device);
746         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
747                 connectionState != BluetoothProfile.STATE_CONNECTING) {
748             return false;
749         }
750 
751         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
752         msg.arg1 = index;
753         sm.sendMessage(msg);
754         return true;
755     }
756 
dial(BluetoothDevice device, String number)757     BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
758         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
759         HeadsetClientStateMachine sm = getStateMachine(device);
760         if (sm == null) {
761             Log.e(TAG, "Cannot allocate SM for device " + device);
762             return null;
763         }
764 
765         int connectionState = sm.getConnectionState(device);
766         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
767                 connectionState != BluetoothProfile.STATE_CONNECTING) {
768             return null;
769         }
770 
771         BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(
772             device, HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID,
773             BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false  /* multiparty */,
774             true  /* outgoing */);
775         Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
776         msg.obj = call;
777         sm.sendMessage(msg);
778         return call;
779     }
780 
sendDTMF(BluetoothDevice device, byte code)781     public boolean sendDTMF(BluetoothDevice device, byte code) {
782         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
783         HeadsetClientStateMachine sm = getStateMachine(device);
784         if (sm == null) {
785             Log.e(TAG, "Cannot allocate SM for device " + device);
786             return false;
787         }
788 
789         int connectionState = sm.getConnectionState(device);
790         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
791                 connectionState != BluetoothProfile.STATE_CONNECTING) {
792             return false;
793         }
794         Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
795         msg.arg1 = code;
796         sm.sendMessage(msg);
797         return true;
798     }
799 
getLastVoiceTagNumber(BluetoothDevice device)800     public boolean getLastVoiceTagNumber(BluetoothDevice device) {
801         return false;
802     }
803 
getCurrentCalls(BluetoothDevice device)804     public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
805         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
806         HeadsetClientStateMachine sm = getStateMachine(device);
807         if (sm == null) {
808             Log.e(TAG, "Cannot allocate SM for device " + device);
809             return null;
810         }
811 
812         int connectionState = sm.getConnectionState(device);
813         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
814             return null;
815         }
816         return sm.getCurrentCalls();
817     }
818 
explicitCallTransfer(BluetoothDevice device)819     public boolean explicitCallTransfer(BluetoothDevice device) {
820         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
821         HeadsetClientStateMachine sm = getStateMachine(device);
822         if (sm == null) {
823             Log.e(TAG, "Cannot allocate SM for device " + device);
824             return false;
825         }
826 
827         int connectionState = sm.getConnectionState(device);
828         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
829                 connectionState != BluetoothProfile.STATE_CONNECTING) {
830             return false;
831         }
832         Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
833         sm.sendMessage(msg);
834         return true;
835     }
836 
getCurrentAgEvents(BluetoothDevice device)837     public Bundle getCurrentAgEvents(BluetoothDevice device) {
838         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
839         HeadsetClientStateMachine sm = getStateMachine(device);
840         if (sm == null) {
841             Log.e(TAG, "Cannot allocate SM for device " + device);
842             return null;
843         }
844 
845         int connectionState = sm.getConnectionState(device);
846         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
847             return null;
848         }
849         return sm.getCurrentAgEvents();
850     }
851 
getCurrentAgFeatures(BluetoothDevice device)852     public Bundle getCurrentAgFeatures(BluetoothDevice device) {
853         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
854         HeadsetClientStateMachine sm = getStateMachine(device);
855         if (sm == null) {
856             Log.e(TAG, "Cannot allocate SM for device " + device);
857             return null;
858         }
859         int connectionState = sm.getConnectionState(device);
860         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
861             return null;
862         }
863         return sm.getCurrentAgFeatures();
864     }
865 
866     // Handle messages from native (JNI) to java
messageFromNative(StackEvent stackEvent)867     public void messageFromNative(StackEvent stackEvent) {
868         HeadsetClientStateMachine sm = getStateMachine(stackEvent.device);
869         if (sm == null) {
870             Log.w(TAG, "No SM found for event " + stackEvent);
871         }
872 
873         sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);
874     }
875 
876     // State machine management
getStateMachine(BluetoothDevice device)877     private synchronized HeadsetClientStateMachine getStateMachine(BluetoothDevice device) {
878         if (device == null) {
879             Log.e(TAG, "getStateMachine failed: Device cannot be null");
880             return null;
881         }
882 
883         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
884         if (sm != null) {
885             if (DBG) {
886                 Log.d(TAG, "Found SM for device " + device);
887             }
888             return sm;
889         }
890 
891         // There is a possibility of a DOS attack if someone populates here with a lot of fake
892         // BluetoothAddresses. If it so happens instead of blowing up we can atleast put a limit on
893         // how long the attack would survive
894         if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) {
895             Log.e(TAG, "Max state machines reached, possible DOS attack " +
896                 MAX_STATE_MACHINES_POSSIBLE);
897             return null;
898         }
899 
900         // Allocate a new SM
901         Log.d(TAG, "Creating a new state machine");
902         sm = mSmFactory.make(this, mSmThread);
903         mStateMachineMap.put(device, sm);
904         return sm;
905     }
906 
907     // Check if any of the state machines have routed the SCO audio stream.
isScoRouted()908     synchronized boolean isScoRouted() {
909         for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry :
910                 mStateMachineMap.entrySet()) {
911             if (entry.getValue() != null) {
912                 int audioState = entry.getValue().getAudioState(entry.getKey());
913                 if (audioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) {
914                     if (DBG) {
915                         Log.d(TAG, "Device " + entry.getKey() + " audio state " + audioState
916                                         + " Connected");
917                     }
918                     return true;
919                 }
920             }
921         }
922         return false;
923     }
924 
925     @Override
dump(StringBuilder sb)926     public synchronized void dump(StringBuilder sb) {
927         super.dump(sb);
928         for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
929             if (sm != null) {
930                 println(sb, "State machine:");
931                 println(sb, "=============");
932                 sm.dump(sb);
933             }
934         }
935     }
936 
937     // For testing
getStateMachineMap()938     protected synchronized Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() {
939         return mStateMachineMap;
940     }
941 
setSMFactory(HeadsetClientStateMachineFactory factory)942     protected void setSMFactory(HeadsetClientStateMachineFactory factory) {
943         mSmFactory = factory;
944     }
945 }
946