• 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.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 = new NativeInterface();
84         mNativeInterface.initializeNative();
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.cleanupNative();
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
setPriority(BluetoothDevice device, int priority)260         public boolean setPriority(BluetoothDevice device, int priority) {
261             HeadsetClientService service = getService();
262             if (service == null) {
263                 return false;
264             }
265             return service.setPriority(device, priority);
266         }
267 
268         @Override
getPriority(BluetoothDevice device)269         public int getPriority(BluetoothDevice device) {
270             HeadsetClientService service = getService();
271             if (service == null) {
272                 return BluetoothProfile.PRIORITY_UNDEFINED;
273             }
274             return service.getPriority(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
getCurrentAgFeatures(BluetoothDevice device)434         public Bundle getCurrentAgFeatures(BluetoothDevice device) {
435             HeadsetClientService service = getService();
436             if (service == null) {
437                 return null;
438             }
439             return service.getCurrentAgFeatures(device);
440         }
441     }
442 
443     ;
444 
445     // API methods
getHeadsetClientService()446     public static synchronized HeadsetClientService getHeadsetClientService() {
447         if (sHeadsetClientService == null) {
448             Log.w(TAG, "getHeadsetClientService(): service is null");
449             return null;
450         }
451         if (!sHeadsetClientService.isAvailable()) {
452             Log.w(TAG, "getHeadsetClientService(): service is not available ");
453             return null;
454         }
455         return sHeadsetClientService;
456     }
457 
setHeadsetClientService(HeadsetClientService instance)458     private static synchronized void setHeadsetClientService(HeadsetClientService instance) {
459         if (DBG) {
460             Log.d(TAG, "setHeadsetClientService(): set to: " + instance);
461         }
462         sHeadsetClientService = instance;
463     }
464 
connect(BluetoothDevice device)465     public boolean connect(BluetoothDevice device) {
466         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
467         if (DBG) {
468             Log.d(TAG, "connect " + device);
469         }
470         HeadsetClientStateMachine sm = getStateMachine(device);
471         if (sm == null) {
472             Log.e(TAG, "Cannot allocate SM for device " + device);
473             return false;
474         }
475 
476         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
477             Log.w(TAG, "Connection not allowed: <" + device.getAddress() + "> is PRIORITY_OFF");
478             return false;
479         }
480 
481         sm.sendMessage(HeadsetClientStateMachine.CONNECT, device);
482         return true;
483     }
484 
disconnect(BluetoothDevice device)485     boolean disconnect(BluetoothDevice device) {
486         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
487         HeadsetClientStateMachine sm = getStateMachine(device);
488         if (sm == null) {
489             Log.e(TAG, "Cannot allocate SM for device " + device);
490             return false;
491         }
492 
493         int connectionState = sm.getConnectionState(device);
494         if (connectionState != BluetoothProfile.STATE_CONNECTED
495                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
496             return false;
497         }
498 
499         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
500         return true;
501     }
502 
getConnectedDevices()503     public synchronized List<BluetoothDevice> getConnectedDevices() {
504         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
505 
506         ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>();
507         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
508             HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
509             if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) {
510                 connectedDevices.add(bd);
511             }
512         }
513         return connectedDevices;
514     }
515 
getDevicesMatchingConnectionStates(int[] states)516     private synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
517         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
518         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
519         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
520             for (int state : states) {
521                 HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
522                 if (sm != null && sm.getConnectionState(bd) == state) {
523                     devices.add(bd);
524                 }
525             }
526         }
527         return devices;
528     }
529 
getConnectionState(BluetoothDevice device)530     private synchronized int getConnectionState(BluetoothDevice device) {
531         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
532         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
533         if (sm != null) {
534             return sm.getConnectionState(device);
535         }
536         return BluetoothProfile.STATE_DISCONNECTED;
537     }
538 
setPriority(BluetoothDevice device, int priority)539     public boolean setPriority(BluetoothDevice device, int priority) {
540         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
541         if (DBG) {
542             Log.d(TAG, "Saved priority " + device + " = " + priority);
543         }
544         AdapterService.getAdapterService().getDatabase()
545                 .setProfilePriority(device, BluetoothProfile.HEADSET_CLIENT, priority);
546         return true;
547     }
548 
getPriority(BluetoothDevice device)549     public int getPriority(BluetoothDevice device) {
550         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
551         return AdapterService.getAdapterService().getDatabase()
552                 .getProfilePriority(device, BluetoothProfile.HEADSET_CLIENT);
553     }
554 
startVoiceRecognition(BluetoothDevice device)555     boolean startVoiceRecognition(BluetoothDevice device) {
556         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
557         HeadsetClientStateMachine sm = getStateMachine(device);
558         if (sm == null) {
559             Log.e(TAG, "Cannot allocate SM for device " + device);
560             return false;
561         }
562         int connectionState = sm.getConnectionState(device);
563         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
564             return false;
565         }
566         sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START);
567         return true;
568     }
569 
stopVoiceRecognition(BluetoothDevice device)570     boolean stopVoiceRecognition(BluetoothDevice device) {
571         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
572         HeadsetClientStateMachine sm = getStateMachine(device);
573         if (sm == null) {
574             Log.e(TAG, "Cannot allocate SM for device " + device);
575             return false;
576         }
577         int connectionState = sm.getConnectionState(device);
578         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
579             return false;
580         }
581         sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_STOP);
582         return true;
583     }
584 
getAudioState(BluetoothDevice device)585     int getAudioState(BluetoothDevice device) {
586         HeadsetClientStateMachine sm = getStateMachine(device);
587         if (sm == null) {
588             Log.e(TAG, "Cannot allocate SM for device " + device);
589             return -1;
590         }
591 
592         return sm.getAudioState(device);
593     }
594 
connectAudio(BluetoothDevice device)595     boolean connectAudio(BluetoothDevice device) {
596         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
597         HeadsetClientStateMachine sm = getStateMachine(device);
598         if (sm == null) {
599             Log.e(TAG, "Cannot allocate SM for device " + device);
600             return false;
601         }
602 
603         if (!sm.isConnected()) {
604             return false;
605         }
606         if (sm.isAudioOn()) {
607             return false;
608         }
609         sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
610         return true;
611     }
612 
disconnectAudio(BluetoothDevice device)613     boolean disconnectAudio(BluetoothDevice device) {
614         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
615         HeadsetClientStateMachine sm = getStateMachine(device);
616         if (sm == null) {
617             Log.e(TAG, "Cannot allocate SM for device " + device);
618             return false;
619         }
620 
621         if (!sm.isAudioOn()) {
622             return false;
623         }
624         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
625         return true;
626     }
627 
holdCall(BluetoothDevice device)628     boolean holdCall(BluetoothDevice device) {
629         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH 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         int connectionState = sm.getConnectionState(device);
637         if (connectionState != BluetoothProfile.STATE_CONNECTED
638                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
639             return false;
640         }
641         Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
642         sm.sendMessage(msg);
643         return true;
644     }
645 
acceptCall(BluetoothDevice device, int flag)646     boolean acceptCall(BluetoothDevice device, int flag) {
647         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
648         /* Phonecalls from a single device are supported, hang up any calls on the other phone */
649         synchronized (this) {
650             for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap
651                     .entrySet()) {
652                 if (entry.getValue() == null || entry.getKey().equals(device)) {
653                     continue;
654                 }
655                 int connectionState = entry.getValue().getConnectionState(entry.getKey());
656                 if (DBG) {
657                     Log.d(TAG,
658                             "Accepting a call on device " + device + ". Possibly disconnecting on "
659                                     + entry.getValue());
660                 }
661                 if (connectionState == BluetoothProfile.STATE_CONNECTED) {
662                     entry.getValue()
663                             .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL)
664                             .sendToTarget();
665                 }
666             }
667         }
668         HeadsetClientStateMachine sm = getStateMachine(device);
669         if (sm == null) {
670             Log.e(TAG, "Cannot allocate SM for device " + device);
671             return false;
672         }
673 
674         int connectionState = sm.getConnectionState(device);
675         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
676             return false;
677         }
678         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
679         msg.arg1 = flag;
680         sm.sendMessage(msg);
681         return true;
682     }
683 
rejectCall(BluetoothDevice device)684     boolean rejectCall(BluetoothDevice device) {
685         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
686         HeadsetClientStateMachine sm = getStateMachine(device);
687         if (sm == null) {
688             Log.e(TAG, "Cannot allocate SM for device " + device);
689             return false;
690         }
691 
692         int connectionState = sm.getConnectionState(device);
693         if (connectionState != BluetoothProfile.STATE_CONNECTED
694                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
695             return false;
696         }
697 
698         Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
699         sm.sendMessage(msg);
700         return true;
701     }
702 
terminateCall(BluetoothDevice device, UUID uuid)703     boolean terminateCall(BluetoothDevice device, UUID uuid) {
704         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
705         HeadsetClientStateMachine sm = getStateMachine(device);
706         if (sm == null) {
707             Log.e(TAG, "Cannot allocate SM for device " + device);
708             return false;
709         }
710 
711         int connectionState = sm.getConnectionState(device);
712         if (connectionState != BluetoothProfile.STATE_CONNECTED
713                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
714             return false;
715         }
716 
717         Message msg = sm.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
718         msg.obj = uuid;
719         sm.sendMessage(msg);
720         return true;
721     }
722 
enterPrivateMode(BluetoothDevice device, int index)723     boolean enterPrivateMode(BluetoothDevice device, int index) {
724         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
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                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
734             return false;
735         }
736 
737         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
738         msg.arg1 = index;
739         sm.sendMessage(msg);
740         return true;
741     }
742 
dial(BluetoothDevice device, String number)743     BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
744         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
745         HeadsetClientStateMachine sm = getStateMachine(device);
746         if (sm == null) {
747             Log.e(TAG, "Cannot allocate SM for device " + device);
748             return null;
749         }
750 
751         int connectionState = sm.getConnectionState(device);
752         if (connectionState != BluetoothProfile.STATE_CONNECTED
753                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
754             return null;
755         }
756 
757         BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(device,
758                 HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID,
759                 BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false  /* multiparty */,
760                 true  /* outgoing */, sm.getInBandRing());
761         Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
762         msg.obj = call;
763         sm.sendMessage(msg);
764         return call;
765     }
766 
sendDTMF(BluetoothDevice device, byte code)767     public boolean sendDTMF(BluetoothDevice device, byte code) {
768         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
769         HeadsetClientStateMachine sm = getStateMachine(device);
770         if (sm == null) {
771             Log.e(TAG, "Cannot allocate SM for device " + device);
772             return false;
773         }
774 
775         int connectionState = sm.getConnectionState(device);
776         if (connectionState != BluetoothProfile.STATE_CONNECTED
777                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
778             return false;
779         }
780         Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
781         msg.arg1 = code;
782         sm.sendMessage(msg);
783         return true;
784     }
785 
getLastVoiceTagNumber(BluetoothDevice device)786     public boolean getLastVoiceTagNumber(BluetoothDevice device) {
787         return false;
788     }
789 
getCurrentCalls(BluetoothDevice device)790     public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
791         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
792         HeadsetClientStateMachine sm = getStateMachine(device);
793         if (sm == null) {
794             Log.e(TAG, "Cannot allocate SM for device " + device);
795             return null;
796         }
797 
798         int connectionState = sm.getConnectionState(device);
799         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
800             return null;
801         }
802         return sm.getCurrentCalls();
803     }
804 
explicitCallTransfer(BluetoothDevice device)805     public boolean explicitCallTransfer(BluetoothDevice device) {
806         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
807         HeadsetClientStateMachine sm = getStateMachine(device);
808         if (sm == null) {
809             Log.e(TAG, "Cannot allocate SM for device " + device);
810             return false;
811         }
812 
813         int connectionState = sm.getConnectionState(device);
814         if (connectionState != BluetoothProfile.STATE_CONNECTED
815                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
816             return false;
817         }
818         Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
819         sm.sendMessage(msg);
820         return true;
821     }
822 
getCurrentAgEvents(BluetoothDevice device)823     public Bundle getCurrentAgEvents(BluetoothDevice device) {
824         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
825         HeadsetClientStateMachine sm = getStateMachine(device);
826         if (sm == null) {
827             Log.e(TAG, "Cannot allocate SM for device " + device);
828             return null;
829         }
830 
831         int connectionState = sm.getConnectionState(device);
832         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
833             return null;
834         }
835         return sm.getCurrentAgEvents();
836     }
837 
getCurrentAgFeatures(BluetoothDevice device)838     public Bundle getCurrentAgFeatures(BluetoothDevice device) {
839         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
840         HeadsetClientStateMachine sm = getStateMachine(device);
841         if (sm == null) {
842             Log.e(TAG, "Cannot allocate SM for device " + device);
843             return null;
844         }
845         int connectionState = sm.getConnectionState(device);
846         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
847             return null;
848         }
849         return sm.getCurrentAgFeatures();
850     }
851 
852     // Handle messages from native (JNI) to java
messageFromNative(StackEvent stackEvent)853     public void messageFromNative(StackEvent stackEvent) {
854         HeadsetClientStateMachine sm = getStateMachine(stackEvent.device);
855         if (sm == null) {
856             Log.w(TAG, "No SM found for event " + stackEvent);
857         }
858 
859         sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);
860     }
861 
862     // State machine management
getStateMachine(BluetoothDevice device)863     private synchronized HeadsetClientStateMachine getStateMachine(BluetoothDevice device) {
864         if (device == null) {
865             Log.e(TAG, "getStateMachine failed: Device cannot be null");
866             return null;
867         }
868 
869         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
870         if (sm != null) {
871             if (DBG) {
872                 Log.d(TAG, "Found SM for device " + device);
873             }
874             return sm;
875         }
876 
877         // There is a possibility of a DOS attack if someone populates here with a lot of fake
878         // BluetoothAddresses. If it so happens instead of blowing up we can atleast put a limit on
879         // how long the attack would survive
880         if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) {
881             Log.e(TAG, "Max state machines reached, possible DOS attack "
882                     + MAX_STATE_MACHINES_POSSIBLE);
883             return null;
884         }
885 
886         // Allocate a new SM
887         Log.d(TAG, "Creating a new state machine");
888         sm = mSmFactory.make(this, mSmThread);
889         mStateMachineMap.put(device, sm);
890         return sm;
891     }
892 
893     // Check if any of the state machines have routed the SCO audio stream.
isScoRouted()894     synchronized boolean isScoRouted() {
895         for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap
896                 .entrySet()) {
897             if (entry.getValue() != null) {
898                 int audioState = entry.getValue().getAudioState(entry.getKey());
899                 if (audioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) {
900                     if (DBG) {
901                         Log.d(TAG, "Device " + entry.getKey() + " audio state " + audioState
902                                 + " Connected");
903                     }
904                     return true;
905                 }
906             }
907         }
908         return false;
909     }
910 
911     @Override
dump(StringBuilder sb)912     public synchronized void dump(StringBuilder sb) {
913         super.dump(sb);
914         for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
915             if (sm != null) {
916                 sm.dump(sb);
917             }
918         }
919     }
920 
921     // For testing
getStateMachineMap()922     protected synchronized Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() {
923         return mStateMachineMap;
924     }
925 
setSMFactory(HeadsetClientStateMachineFactory factory)926     protected void setSMFactory(HeadsetClientStateMachineFactory factory) {
927         mSmFactory = factory;
928     }
929 
getAudioManager()930     protected AudioManager getAudioManager() {
931         return mAudioManager;
932     }
933 }
934