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