HeadsetClientService.java revision 1ff2e6e9306272bd113c8633c9b52fb169eddcb3
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.BluetoothProfile;
21import android.bluetooth.BluetoothHeadsetClient;
22import android.bluetooth.BluetoothHeadsetClientCall;
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.Message;
31import android.provider.Settings;
32import android.util.Log;
33
34import com.android.bluetooth.btservice.ProfileService;
35import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService;
36import com.android.bluetooth.Utils;
37
38import java.util.ArrayList;
39import java.util.List;
40import java.util.UUID;
41
42/**
43 * Provides Bluetooth Headset Client (HF Role) profile, as a service in the
44 * Bluetooth application.
45 *
46 * @hide
47 */
48public class HeadsetClientService extends ProfileService {
49    private static final boolean DBG = false;
50    private static final String TAG = "HeadsetClientService";
51
52    private HeadsetClientStateMachine mStateMachine;
53    private static HeadsetClientService sHeadsetClientService;
54
55    public static String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag";
56
57    @Override
58    protected String getName() {
59        return TAG;
60    }
61
62    @Override
63    public IProfileServiceBinder initBinder() {
64        return new BluetoothHeadsetClientBinder(this);
65    }
66
67    @Override
68    protected boolean start() {
69        mStateMachine = HeadsetClientStateMachine.make(this);
70        IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
71        filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
72        try {
73            registerReceiver(mBroadcastReceiver, filter);
74        } catch (Exception e) {
75            Log.w(TAG, "Unable to register broadcat receiver", e);
76        }
77        setHeadsetClientService(this);
78
79        // Start the HfpClientConnectionService to create connection with telecom when HFP
80        // connection is available.
81        Intent startIntent = new Intent(this, HfpClientConnectionService.class);
82        startService(startIntent);
83
84        return true;
85    }
86
87    @Override
88    protected boolean stop() {
89        try {
90            unregisterReceiver(mBroadcastReceiver);
91        } catch (Exception e) {
92            Log.w(TAG, "Unable to unregister broadcast receiver", e);
93        }
94
95        if (mStateMachine != null) {
96            mStateMachine.doQuit();
97        }
98
99        // Stop the HfpClientConnectionService.
100        Intent stopIntent = new Intent(this, HfpClientConnectionService.class);
101        stopIntent.putExtra(HFP_CLIENT_STOP_TAG, true);
102        startService(stopIntent);
103        return true;
104    }
105
106    @Override
107    protected boolean cleanup() {
108        if (mStateMachine != null) {
109            mStateMachine.cleanup();
110        }
111        clearHeadsetClientService();
112        return true;
113    }
114
115    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
116        @Override
117        public void onReceive(Context context, Intent intent) {
118            String action = intent.getAction();
119
120            // We handle the volume changes for Voice calls here since HFP audio volume control does
121            // not go through audio manager (audio mixer). We check if the voice call volume has
122            // changed and subsequently change the SCO volume see
123            // ({@link HeadsetClientStateMachine#SET_SPEAKER_VOLUME} in
124            // {@link HeadsetClientStateMachine} for details.
125            if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
126                Log.d(TAG, "Volume changed for stream: " +
127                    intent.getExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE));
128                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
129                if (streamType == AudioManager.STREAM_VOICE_CALL) {
130                    int streamValue = intent
131                            .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
132                    int streamPrevValue = intent.getIntExtra(
133                            AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
134
135                    if (streamValue != -1 && streamValue != streamPrevValue) {
136                        mStateMachine.sendMessage(mStateMachine.obtainMessage(
137                                HeadsetClientStateMachine.SET_SPEAKER_VOLUME, streamValue, 0));
138                    }
139                }
140            }
141        }
142    };
143
144    /**
145     * Handlers for incoming service calls
146     */
147    private static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
148            implements IProfileServiceBinder {
149        private HeadsetClientService mService;
150
151        public BluetoothHeadsetClientBinder(HeadsetClientService svc) {
152            mService = svc;
153        }
154
155        @Override
156        public boolean cleanup() {
157            mService = null;
158            return true;
159        }
160
161        private HeadsetClientService getService() {
162            if (!Utils.checkCaller()) {
163                Log.w(TAG, "HeadsetClient call not allowed for non-active user");
164                return null;
165            }
166
167            if (mService != null && mService.isAvailable()) {
168                return mService;
169            }
170            return null;
171        }
172
173        @Override
174        public boolean connect(BluetoothDevice device) {
175            HeadsetClientService service = getService();
176            if (service == null) {
177                return false;
178            }
179            return service.connect(device);
180        }
181
182        @Override
183        public boolean disconnect(BluetoothDevice device) {
184            HeadsetClientService service = getService();
185            if (service == null) {
186                return false;
187            }
188            return service.disconnect(device);
189        }
190
191        @Override
192        public List<BluetoothDevice> getConnectedDevices() {
193            HeadsetClientService service = getService();
194            if (service == null) {
195                return new ArrayList<BluetoothDevice>(0);
196            }
197            return service.getConnectedDevices();
198        }
199
200        @Override
201        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
202            HeadsetClientService service = getService();
203            if (service == null) {
204                return new ArrayList<BluetoothDevice>(0);
205            }
206            return service.getDevicesMatchingConnectionStates(states);
207        }
208
209        @Override
210        public int getConnectionState(BluetoothDevice device) {
211            HeadsetClientService service = getService();
212            if (service == null) {
213                return BluetoothProfile.STATE_DISCONNECTED;
214            }
215            return service.getConnectionState(device);
216        }
217
218        @Override
219        public boolean setPriority(BluetoothDevice device, int priority) {
220            HeadsetClientService service = getService();
221            if (service == null) {
222                return false;
223            }
224            return service.setPriority(device, priority);
225        }
226
227        @Override
228        public int getPriority(BluetoothDevice device) {
229            HeadsetClientService service = getService();
230            if (service == null) {
231                return BluetoothProfile.PRIORITY_UNDEFINED;
232            }
233            return service.getPriority(device);
234        }
235
236        @Override
237        public boolean startVoiceRecognition(BluetoothDevice device) {
238            HeadsetClientService service = getService();
239            if (service == null) {
240                return false;
241            }
242            return service.startVoiceRecognition(device);
243        }
244
245        @Override
246        public boolean stopVoiceRecognition(BluetoothDevice device) {
247            HeadsetClientService service = getService();
248            if (service == null) {
249                return false;
250            }
251            return service.stopVoiceRecognition(device);
252        }
253
254        @Override
255        public boolean acceptIncomingConnect(BluetoothDevice device) {
256            HeadsetClientService service = getService();
257            if (service == null) {
258                return false;
259            }
260            return service.acceptIncomingConnect(device);
261        }
262
263        @Override
264        public boolean rejectIncomingConnect(BluetoothDevice device) {
265            HeadsetClientService service = getService();
266            if (service == null) {
267                return false;
268            }
269            return service.rejectIncomingConnect(device);
270        }
271
272        @Override
273        public int getAudioState(BluetoothDevice device) {
274            HeadsetClientService service = getService();
275            if (service == null) {
276                return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
277            }
278            return service.getAudioState(device);
279        }
280
281        @Override
282        public void setAudioRouteAllowed(boolean allowed) {
283            HeadsetClientService service = getService();
284            if (service != null) {
285                service.setAudioRouteAllowed(allowed);
286            }
287        }
288
289        @Override
290        public boolean getAudioRouteAllowed() {
291            HeadsetClientService service = getService();
292            if (service != null) {
293                return service.getAudioRouteAllowed();
294            }
295
296            return false;
297        }
298
299        @Override
300        public boolean connectAudio() {
301            HeadsetClientService service = getService();
302            if (service == null) {
303                return false;
304            }
305            return service.connectAudio();
306        }
307
308        @Override
309        public boolean disconnectAudio() {
310            HeadsetClientService service = getService();
311            if (service == null) {
312                return false;
313            }
314            return service.disconnectAudio();
315        }
316
317        @Override
318        public boolean acceptCall(BluetoothDevice device, int flag) {
319            HeadsetClientService service = getService();
320            if (service == null) {
321                return false;
322            }
323            return service.acceptCall(device, flag);
324        }
325
326        @Override
327        public boolean rejectCall(BluetoothDevice device) {
328            HeadsetClientService service = getService();
329            if (service == null) {
330                return false;
331            }
332            return service.rejectCall(device);
333        }
334
335        @Override
336        public boolean holdCall(BluetoothDevice device) {
337            HeadsetClientService service = getService();
338            if (service == null) {
339                return false;
340            }
341            return service.holdCall(device);
342        }
343
344        @Override
345        public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
346            HeadsetClientService service = getService();
347            if (service == null) {
348                Log.w(TAG, "service is null");
349                return false;
350            }
351            return service.terminateCall(device, call.getUUID());
352        }
353
354        @Override
355        public boolean explicitCallTransfer(BluetoothDevice device) {
356            HeadsetClientService service = getService();
357            if (service == null) {
358                return false;
359            }
360            return service.explicitCallTransfer(device);
361        }
362
363        @Override
364        public boolean enterPrivateMode(BluetoothDevice device, int index) {
365            HeadsetClientService service = getService();
366            if (service == null) {
367                return false;
368            }
369            return service.enterPrivateMode(device, index);
370        }
371
372        @Override
373        public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
374            HeadsetClientService service = getService();
375            if (service == null) {
376                return null;
377            }
378            return service.dial(device, number);
379        }
380
381        @Override
382        public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
383            HeadsetClientService service = getService();
384            if (service == null) {
385                return null;
386            }
387            return service.getCurrentCalls(device);
388        }
389
390        @Override
391        public boolean sendDTMF(BluetoothDevice device, byte code) {
392            HeadsetClientService service = getService();
393            if (service == null) {
394                return false;
395            }
396            return service.sendDTMF(device, code);
397        }
398
399        @Override
400        public boolean getLastVoiceTagNumber(BluetoothDevice device) {
401            HeadsetClientService service = getService();
402            if (service == null) {
403                return false;
404            }
405            return service.getLastVoiceTagNumber(device);
406        }
407
408        @Override
409        public Bundle getCurrentAgEvents(BluetoothDevice device) {
410            HeadsetClientService service = getService();
411            if (service == null) {
412                return null;
413            }
414            return service.getCurrentAgEvents(device);
415        }
416
417        @Override
418        public Bundle getCurrentAgFeatures(BluetoothDevice device) {
419            HeadsetClientService service = getService();
420            if (service == null) {
421                return null;
422            }
423            return service.getCurrentAgFeatures(device);
424        }
425    };
426
427    // API methods
428    public static synchronized HeadsetClientService getHeadsetClientService() {
429        if (sHeadsetClientService != null && sHeadsetClientService.isAvailable()) {
430            if (DBG) {
431                Log.d(TAG, "getHeadsetClientService(): returning " + sHeadsetClientService);
432            }
433            return sHeadsetClientService;
434        }
435        if (DBG) {
436            if (sHeadsetClientService == null) {
437                Log.d(TAG, "getHeadsetClientService(): service is NULL");
438            } else if (!(sHeadsetClientService.isAvailable())) {
439                Log.d(TAG, "getHeadsetClientService(): service is not available");
440            }
441        }
442        return null;
443    }
444
445    private static synchronized void setHeadsetClientService(HeadsetClientService instance) {
446        if (instance != null && instance.isAvailable()) {
447            if (DBG) {
448                Log.d(TAG, "setHeadsetClientService(): set to: " + sHeadsetClientService);
449            }
450            sHeadsetClientService = instance;
451        } else {
452            if (DBG) {
453                if (sHeadsetClientService == null) {
454                    Log.d(TAG, "setHeadsetClientService(): service not available");
455                } else if (!sHeadsetClientService.isAvailable()) {
456                    Log.d(TAG, "setHeadsetClientService(): service is cleaning up");
457                }
458            }
459        }
460    }
461
462    private static synchronized void clearHeadsetClientService() {
463        sHeadsetClientService = null;
464    }
465
466    public boolean connect(BluetoothDevice device) {
467        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
468                "Need BLUETOOTH ADMIN permission");
469
470        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
471            return false;
472        }
473
474        int connectionState = mStateMachine.getConnectionState(device);
475        if (connectionState == BluetoothProfile.STATE_CONNECTED ||
476                connectionState == BluetoothProfile.STATE_CONNECTING) {
477            return false;
478        }
479
480        mStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT, device);
481        return true;
482    }
483
484    boolean disconnect(BluetoothDevice device) {
485        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
486                "Need BLUETOOTH ADMIN permission");
487        int connectionState = mStateMachine.getConnectionState(device);
488        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
489                connectionState != BluetoothProfile.STATE_CONNECTING) {
490            return false;
491        }
492
493        mStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
494        return true;
495    }
496
497    public List<BluetoothDevice> getConnectedDevices() {
498        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
499        return mStateMachine.getConnectedDevices();
500    }
501
502    private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
503        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
504        return mStateMachine.getDevicesMatchingConnectionStates(states);
505    }
506
507    int getConnectionState(BluetoothDevice device) {
508        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
509        return mStateMachine.getConnectionState(device);
510    }
511
512    // TODO Should new setting for HeadsetClient priority be created?
513    public boolean setPriority(BluetoothDevice device, int priority) {
514        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
515                "Need BLUETOOTH_ADMIN permission");
516        Settings.Global.putInt(getContentResolver(),
517                Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
518                priority);
519        if (DBG) {
520            Log.d(TAG, "Saved priority " + device + " = " + priority);
521        }
522        return true;
523    }
524
525    public int getPriority(BluetoothDevice device) {
526        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
527                "Need BLUETOOTH_ADMIN permission");
528        int priority = Settings.Global.getInt(getContentResolver(),
529                Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
530                BluetoothProfile.PRIORITY_UNDEFINED);
531        return priority;
532    }
533
534    boolean startVoiceRecognition(BluetoothDevice device) {
535        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
536        int connectionState = mStateMachine.getConnectionState(device);
537        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
538                connectionState != BluetoothProfile.STATE_CONNECTING) {
539            return false;
540        }
541        mStateMachine.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START);
542        return true;
543    }
544
545    boolean stopVoiceRecognition(BluetoothDevice device) {
546        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
547        // It seem that we really need to check the AudioOn state.
548        // But since we allow startVoiceRecognition in STATE_CONNECTED and
549        // STATE_CONNECTING state, we do these 2 in this method
550        int connectionState = mStateMachine.getConnectionState(device);
551        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
552                connectionState != BluetoothProfile.STATE_CONNECTING) {
553            return false;
554        }
555        mStateMachine.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_STOP);
556        return true;
557    }
558
559    boolean acceptIncomingConnect(BluetoothDevice device) {
560        // TODO(BT) remove it if stack does access control
561        return false;
562    }
563
564    boolean rejectIncomingConnect(BluetoothDevice device) {
565        // TODO(BT) remove it if stack does access control
566        return false;
567    }
568
569    int getAudioState(BluetoothDevice device) {
570        return mStateMachine.getAudioState(device);
571    }
572
573    public void setAudioRouteAllowed(boolean allowed) {
574        mStateMachine.setAudioRouteAllowed(allowed);
575    }
576
577    public boolean getAudioRouteAllowed() {
578        return mStateMachine.getAudioRouteAllowed();
579    }
580
581    boolean connectAudio() {
582        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
583        if (!mStateMachine.isConnected()) {
584            return false;
585        }
586        if (mStateMachine.isAudioOn()) {
587            return false;
588        }
589        mStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
590        return true;
591    }
592
593    boolean disconnectAudio() {
594        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
595        if (!mStateMachine.isAudioOn()) {
596            return false;
597        }
598        mStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
599        return true;
600    }
601
602    boolean holdCall(BluetoothDevice device) {
603        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
604        int connectionState = mStateMachine.getConnectionState(device);
605        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
606                connectionState != BluetoothProfile.STATE_CONNECTING) {
607            return false;
608        }
609        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
610        mStateMachine.sendMessage(msg);
611        return true;
612    }
613
614    boolean acceptCall(BluetoothDevice device, int flag) {
615        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
616        int connectionState = mStateMachine.getConnectionState(device);
617        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
618                connectionState != BluetoothProfile.STATE_CONNECTING) {
619            return false;
620        }
621        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
622        msg.arg1 = flag;
623        mStateMachine.sendMessage(msg);
624        return true;
625    }
626
627    boolean rejectCall(BluetoothDevice device) {
628        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
629        int connectionState = mStateMachine.getConnectionState(device);
630        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
631                connectionState != BluetoothProfile.STATE_CONNECTING) {
632            return false;
633        }
634
635        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
636        mStateMachine.sendMessage(msg);
637        return true;
638    }
639
640    boolean terminateCall(BluetoothDevice device, UUID uuid) {
641        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
642        int connectionState = mStateMachine.getConnectionState(device);
643        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
644                connectionState != BluetoothProfile.STATE_CONNECTING) {
645            return false;
646        }
647
648        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
649        msg.obj = uuid;
650        mStateMachine.sendMessage(msg);
651        return true;
652    }
653
654    boolean enterPrivateMode(BluetoothDevice device, int index) {
655        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
656        int connectionState = mStateMachine.getConnectionState(device);
657        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
658                connectionState != BluetoothProfile.STATE_CONNECTING) {
659            return false;
660        }
661
662        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
663        msg.arg1 = index;
664        mStateMachine.sendMessage(msg);
665        return true;
666    }
667
668    BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
669        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
670        int connectionState = mStateMachine.getConnectionState(device);
671        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
672                connectionState != BluetoothProfile.STATE_CONNECTING) {
673            return null;
674        }
675
676        BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(
677            device, HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID,
678            BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false  /* multiparty */,
679            true  /* outgoing */);
680        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
681        msg.obj = call;
682        mStateMachine.sendMessage(msg);
683        return call;
684    }
685
686    public boolean sendDTMF(BluetoothDevice device, byte code) {
687        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
688        int connectionState = mStateMachine.getConnectionState(device);
689        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
690                connectionState != BluetoothProfile.STATE_CONNECTING) {
691            return false;
692        }
693        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
694        msg.arg1 = code;
695        mStateMachine.sendMessage(msg);
696        return true;
697    }
698
699    public boolean getLastVoiceTagNumber(BluetoothDevice device) {
700        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
701        int connectionState = mStateMachine.getConnectionState(device);
702        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
703                connectionState != BluetoothProfile.STATE_CONNECTING) {
704            return false;
705        }
706        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.LAST_VTAG_NUMBER);
707        mStateMachine.sendMessage(msg);
708        return true;
709    }
710
711    public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
712        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
713        int connectionState = mStateMachine.getConnectionState(device);
714        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
715            return null;
716        }
717        return mStateMachine.getCurrentCalls();
718    }
719
720    public boolean explicitCallTransfer(BluetoothDevice device) {
721        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
722        int connectionState = mStateMachine.getConnectionState(device);
723        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
724                connectionState != BluetoothProfile.STATE_CONNECTING) {
725            return false;
726        }
727        Message msg = mStateMachine
728                .obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
729        mStateMachine.sendMessage(msg);
730        return true;
731    }
732
733    public Bundle getCurrentAgEvents(BluetoothDevice device) {
734        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
735        int connectionState = mStateMachine.getConnectionState(device);
736        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
737            return null;
738        }
739        return mStateMachine.getCurrentAgEvents();
740    }
741
742    public Bundle getCurrentAgFeatures(BluetoothDevice device) {
743        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
744        int connectionState = mStateMachine.getConnectionState(device);
745        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
746            return null;
747        }
748        return mStateMachine.getCurrentAgFeatures();
749    }
750
751    @Override
752    public void dump(StringBuilder sb) {
753        super.dump(sb);
754        if (mStateMachine != null) {
755            mStateMachine.dump(sb);
756        }
757    }
758}
759