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