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