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