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