HeadsetClientService.java revision 05462fe44531f887fe91ac173ec645e91b236025
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                return false;
349            }
350            return service.terminateCall(device, call.getUUID());
351        }
352
353        @Override
354        public boolean explicitCallTransfer(BluetoothDevice device) {
355            HeadsetClientService service = getService();
356            if (service == null) {
357                return false;
358            }
359            return service.explicitCallTransfer(device);
360        }
361
362        @Override
363        public boolean enterPrivateMode(BluetoothDevice device, int index) {
364            HeadsetClientService service = getService();
365            if (service == null) {
366                return false;
367            }
368            return service.enterPrivateMode(device, index);
369        }
370
371        @Override
372        public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
373            HeadsetClientService service = getService();
374            if (service == null) {
375                return null;
376            }
377            return service.dial(device, number);
378        }
379
380        @Override
381        public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
382            HeadsetClientService service = getService();
383            if (service == null) {
384                return null;
385            }
386            return service.getCurrentCalls(device);
387        }
388
389        @Override
390        public boolean sendDTMF(BluetoothDevice device, byte code) {
391            HeadsetClientService service = getService();
392            if (service == null) {
393                return false;
394            }
395            return service.sendDTMF(device, code);
396        }
397
398        @Override
399        public boolean getLastVoiceTagNumber(BluetoothDevice device) {
400            HeadsetClientService service = getService();
401            if (service == null) {
402                return false;
403            }
404            return service.getLastVoiceTagNumber(device);
405        }
406
407        @Override
408        public Bundle getCurrentAgEvents(BluetoothDevice device) {
409            HeadsetClientService service = getService();
410            if (service == null) {
411                return null;
412            }
413            return service.getCurrentAgEvents(device);
414        }
415
416        @Override
417        public Bundle getCurrentAgFeatures(BluetoothDevice device) {
418            HeadsetClientService service = getService();
419            if (service == null) {
420                return null;
421            }
422            return service.getCurrentAgFeatures(device);
423        }
424    };
425
426    // API methods
427    public static synchronized HeadsetClientService getHeadsetClientService() {
428        if (sHeadsetClientService != null && sHeadsetClientService.isAvailable()) {
429            if (DBG) {
430                Log.d(TAG, "getHeadsetClientService(): returning " + sHeadsetClientService);
431            }
432            return sHeadsetClientService;
433        }
434        if (DBG) {
435            if (sHeadsetClientService == null) {
436                Log.d(TAG, "getHeadsetClientService(): service is NULL");
437            } else if (!(sHeadsetClientService.isAvailable())) {
438                Log.d(TAG, "getHeadsetClientService(): service is not available");
439            }
440        }
441        return null;
442    }
443
444    private static synchronized void setHeadsetClientService(HeadsetClientService instance) {
445        if (instance != null && instance.isAvailable()) {
446            if (DBG) {
447                Log.d(TAG, "setHeadsetClientService(): set to: " + sHeadsetClientService);
448            }
449            sHeadsetClientService = instance;
450        } else {
451            if (DBG) {
452                if (sHeadsetClientService == null) {
453                    Log.d(TAG, "setHeadsetClientService(): service not available");
454                } else if (!sHeadsetClientService.isAvailable()) {
455                    Log.d(TAG, "setHeadsetClientService(): service is cleaning up");
456                }
457            }
458        }
459    }
460
461    private static synchronized void clearHeadsetClientService() {
462        sHeadsetClientService = null;
463    }
464
465    public boolean connect(BluetoothDevice device) {
466        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
467                "Need BLUETOOTH ADMIN permission");
468
469        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
470            return false;
471        }
472
473        int connectionState = mStateMachine.getConnectionState(device);
474        if (connectionState == BluetoothProfile.STATE_CONNECTED ||
475                connectionState == BluetoothProfile.STATE_CONNECTING) {
476            return false;
477        }
478
479        mStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT, device);
480        return true;
481    }
482
483    boolean disconnect(BluetoothDevice device) {
484        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
485                "Need BLUETOOTH ADMIN permission");
486        int connectionState = mStateMachine.getConnectionState(device);
487        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
488                connectionState != BluetoothProfile.STATE_CONNECTING) {
489            return false;
490        }
491
492        mStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
493        return true;
494    }
495
496    public List<BluetoothDevice> getConnectedDevices() {
497        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
498        return mStateMachine.getConnectedDevices();
499    }
500
501    private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
502        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
503        return mStateMachine.getDevicesMatchingConnectionStates(states);
504    }
505
506    int getConnectionState(BluetoothDevice device) {
507        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
508        return mStateMachine.getConnectionState(device);
509    }
510
511    // TODO Should new setting for HeadsetClient priority be created?
512    public boolean setPriority(BluetoothDevice device, int priority) {
513        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
514                "Need BLUETOOTH_ADMIN permission");
515        Settings.Global.putInt(getContentResolver(),
516                Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
517                priority);
518        if (DBG) {
519            Log.d(TAG, "Saved priority " + device + " = " + priority);
520        }
521        return true;
522    }
523
524    public int getPriority(BluetoothDevice device) {
525        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
526                "Need BLUETOOTH_ADMIN permission");
527        int priority = Settings.Global.getInt(getContentResolver(),
528                Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
529                BluetoothProfile.PRIORITY_UNDEFINED);
530        return priority;
531    }
532
533    boolean startVoiceRecognition(BluetoothDevice device) {
534        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
535        int connectionState = mStateMachine.getConnectionState(device);
536        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
537                connectionState != BluetoothProfile.STATE_CONNECTING) {
538            return false;
539        }
540        mStateMachine.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START);
541        return true;
542    }
543
544    boolean stopVoiceRecognition(BluetoothDevice device) {
545        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
546        // It seem that we really need to check the AudioOn state.
547        // But since we allow startVoiceRecognition in STATE_CONNECTED and
548        // STATE_CONNECTING state, we do these 2 in this method
549        int connectionState = mStateMachine.getConnectionState(device);
550        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
551                connectionState != BluetoothProfile.STATE_CONNECTING) {
552            return false;
553        }
554        mStateMachine.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_STOP);
555        return true;
556    }
557
558    boolean acceptIncomingConnect(BluetoothDevice device) {
559        // TODO(BT) remove it if stack does access control
560        return false;
561    }
562
563    boolean rejectIncomingConnect(BluetoothDevice device) {
564        // TODO(BT) remove it if stack does access control
565        return false;
566    }
567
568    int getAudioState(BluetoothDevice device) {
569        return mStateMachine.getAudioState(device);
570    }
571
572    public void setAudioRouteAllowed(boolean allowed) {
573        mStateMachine.setAudioRouteAllowed(allowed);
574    }
575
576    public boolean getAudioRouteAllowed() {
577        return mStateMachine.getAudioRouteAllowed();
578    }
579
580    boolean connectAudio() {
581        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
582        if (!mStateMachine.isConnected()) {
583            return false;
584        }
585        if (mStateMachine.isAudioOn()) {
586            return false;
587        }
588        mStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
589        return true;
590    }
591
592    boolean disconnectAudio() {
593        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
594        if (!mStateMachine.isAudioOn()) {
595            return false;
596        }
597        mStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
598        return true;
599    }
600
601    boolean holdCall(BluetoothDevice device) {
602        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
603        int connectionState = mStateMachine.getConnectionState(device);
604        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
605                connectionState != BluetoothProfile.STATE_CONNECTING) {
606            return false;
607        }
608        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
609        mStateMachine.sendMessage(msg);
610        return true;
611    }
612
613    boolean acceptCall(BluetoothDevice device, int flag) {
614        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
615        int connectionState = mStateMachine.getConnectionState(device);
616        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
617                connectionState != BluetoothProfile.STATE_CONNECTING) {
618            return false;
619        }
620        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
621        msg.arg1 = flag;
622        mStateMachine.sendMessage(msg);
623        return true;
624    }
625
626    boolean rejectCall(BluetoothDevice device) {
627        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
628        int connectionState = mStateMachine.getConnectionState(device);
629        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
630                connectionState != BluetoothProfile.STATE_CONNECTING) {
631            return false;
632        }
633
634        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
635        mStateMachine.sendMessage(msg);
636        return true;
637    }
638
639    boolean terminateCall(BluetoothDevice device, UUID uuid) {
640        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
641        int connectionState = mStateMachine.getConnectionState(device);
642        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
643                connectionState != BluetoothProfile.STATE_CONNECTING) {
644            return false;
645        }
646
647        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
648        msg.obj = uuid;
649        mStateMachine.sendMessage(msg);
650        return true;
651    }
652
653    boolean enterPrivateMode(BluetoothDevice device, int index) {
654        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
655        int connectionState = mStateMachine.getConnectionState(device);
656        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
657                connectionState != BluetoothProfile.STATE_CONNECTING) {
658            return false;
659        }
660
661        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
662        msg.arg1 = index;
663        mStateMachine.sendMessage(msg);
664        return true;
665    }
666
667    BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
668        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
669        int connectionState = mStateMachine.getConnectionState(device);
670        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
671                connectionState != BluetoothProfile.STATE_CONNECTING) {
672            return null;
673        }
674
675        BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(
676            device, HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID,
677            BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false  /* multiparty */,
678            true  /* outgoing */);
679        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
680        msg.obj = call;
681        mStateMachine.sendMessage(msg);
682        return call;
683    }
684
685    public boolean sendDTMF(BluetoothDevice device, byte code) {
686        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
687        int connectionState = mStateMachine.getConnectionState(device);
688        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
689                connectionState != BluetoothProfile.STATE_CONNECTING) {
690            return false;
691        }
692        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
693        msg.arg1 = code;
694        mStateMachine.sendMessage(msg);
695        return true;
696    }
697
698    public boolean getLastVoiceTagNumber(BluetoothDevice device) {
699        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
700        int connectionState = mStateMachine.getConnectionState(device);
701        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
702                connectionState != BluetoothProfile.STATE_CONNECTING) {
703            return false;
704        }
705        Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.LAST_VTAG_NUMBER);
706        mStateMachine.sendMessage(msg);
707        return true;
708    }
709
710    public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
711        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
712        int connectionState = mStateMachine.getConnectionState(device);
713        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
714            return null;
715        }
716        return mStateMachine.getCurrentCalls();
717    }
718
719    public boolean explicitCallTransfer(BluetoothDevice device) {
720        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
721        int connectionState = mStateMachine.getConnectionState(device);
722        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
723                connectionState != BluetoothProfile.STATE_CONNECTING) {
724            return false;
725        }
726        Message msg = mStateMachine
727                .obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
728        mStateMachine.sendMessage(msg);
729        return true;
730    }
731
732    public Bundle getCurrentAgEvents(BluetoothDevice device) {
733        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
734        int connectionState = mStateMachine.getConnectionState(device);
735        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
736            return null;
737        }
738        return mStateMachine.getCurrentAgEvents();
739    }
740
741    public Bundle getCurrentAgFeatures(BluetoothDevice device) {
742        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
743        int connectionState = mStateMachine.getConnectionState(device);
744        if (connectionState != BluetoothProfile.STATE_CONNECTED) {
745            return null;
746        }
747        return mStateMachine.getCurrentAgFeatures();
748    }
749
750    @Override
751    public void dump(StringBuilder sb) {
752        super.dump(sb);
753        if (mStateMachine != null) {
754            mStateMachine.dump(sb);
755        }
756    }
757}
758