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