Home | History | Annotate | Download | only in hfpclient
      1 /*
      2  * Copyright (c) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.bluetooth.hfpclient;
     18 
     19 import android.bluetooth.BluetoothDevice;
     20 import android.bluetooth.BluetoothProfile;
     21 import android.bluetooth.BluetoothHeadsetClient;
     22 import android.bluetooth.BluetoothHeadsetClientCall;
     23 import android.bluetooth.IBluetoothHeadsetClient;
     24 import android.content.BroadcastReceiver;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.media.AudioManager;
     29 import android.os.Bundle;
     30 import android.os.HandlerThread;
     31 import android.os.Looper;
     32 import android.os.Message;
     33 import android.provider.Settings;
     34 import android.util.Log;
     35 
     36 import com.android.bluetooth.btservice.ProfileService;
     37 import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService;
     38 import com.android.bluetooth.Utils;
     39 
     40 import java.util.ArrayList;
     41 import java.util.HashMap;
     42 import java.util.Iterator;
     43 import java.util.List;
     44 import java.util.Map;
     45 import java.util.UUID;
     46 
     47 /**
     48  * Provides Bluetooth Headset Client (HF Role) profile, as a service in the
     49  * Bluetooth application.
     50  *
     51  * @hide
     52  */
     53 public class HeadsetClientService extends ProfileService {
     54     private static final boolean DBG = false;
     55     private static final String TAG = "HeadsetClientService";
     56 
     57     private HashMap<BluetoothDevice, HeadsetClientStateMachine> mStateMachineMap =
     58         new HashMap<>();
     59     private static HeadsetClientService sHeadsetClientService;
     60     private NativeInterface mNativeInterface = null;
     61     private HandlerThread mSmThread = null;
     62     private HeadsetClientStateMachineFactory mSmFactory = null;
     63     private AudioManager mAudioManager = null;
     64     // Maxinum number of devices we can try connecting to in one session
     65     private static final int MAX_STATE_MACHINES_POSSIBLE = 100;
     66 
     67     public static String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag";
     68 
     69     static {
     70         NativeInterface.classInitNative();
     71     }
     72 
     73     @Override
     74     protected String getName() {
     75         return TAG;
     76     }
     77 
     78     @Override
     79     public IProfileServiceBinder initBinder() {
     80         return new BluetoothHeadsetClientBinder(this);
     81     }
     82 
     83     @Override
     84     protected synchronized boolean start() {
     85         if (DBG) {
     86             Log.d(TAG, "start()");
     87         }
     88         // Setup the JNI service
     89         NativeInterface.initializeNative();
     90         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
     91 
     92         mSmFactory = new HeadsetClientStateMachineFactory();
     93         mStateMachineMap.clear();
     94 
     95         IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
     96         try {
     97             registerReceiver(mBroadcastReceiver, filter);
     98         } catch (Exception e) {
     99             Log.w(TAG, "Unable to register broadcat receiver", e);
    100         }
    101         setHeadsetClientService(this);
    102         mNativeInterface = new NativeInterface();
    103 
    104         // Start the HfpClientConnectionService to create connection with telecom when HFP
    105         // connection is available.
    106         Intent startIntent = new Intent(this, HfpClientConnectionService.class);
    107         startService(startIntent);
    108 
    109         // Create the thread on which all State Machines will run
    110         mSmThread = new HandlerThread("HeadsetClient.SM");
    111         mSmThread.start();
    112         NativeInterface.initializeNative();
    113 
    114         return true;
    115     }
    116 
    117     @Override
    118     protected synchronized boolean stop() {
    119         try {
    120             unregisterReceiver(mBroadcastReceiver);
    121         } catch (Exception e) {
    122             Log.w(TAG, "Unable to unregister broadcast receiver", e);
    123         }
    124 
    125         for (Iterator<Map.Entry<BluetoothDevice, HeadsetClientStateMachine>> it =
    126                 mStateMachineMap.entrySet().iterator(); it.hasNext(); ) {
    127             HeadsetClientStateMachine sm =
    128                 mStateMachineMap.get((BluetoothDevice) it.next().getKey());
    129             sm.doQuit();
    130             it.remove();
    131         }
    132 
    133         // Stop the HfpClientConnectionService.
    134         Intent stopIntent = new Intent(this, HfpClientConnectionService.class);
    135         stopIntent.putExtra(HFP_CLIENT_STOP_TAG, true);
    136         startService(stopIntent);
    137         mNativeInterface = null;
    138 
    139         // Stop the handler thread
    140         mSmThread.quit();
    141         mSmThread = null;
    142 
    143         NativeInterface.cleanupNative();
    144 
    145         return true;
    146     }
    147 
    148     @Override
    149     protected boolean cleanup() {
    150         HeadsetClientStateMachine.cleanup();
    151         clearHeadsetClientService();
    152         return true;
    153     }
    154 
    155     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    156         @Override
    157         public void onReceive(Context context, Intent intent) {
    158             String action = intent.getAction();
    159 
    160             // We handle the volume changes for Voice calls here since HFP audio volume control does
    161             // not go through audio manager (audio mixer). see
    162             // ({@link HeadsetClientStateMachine#SET_SPEAKER_VOLUME} in
    163             // {@link HeadsetClientStateMachine} for details.
    164             if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
    165                 Log.d(TAG, "Volume changed for stream: " +
    166                     intent.getExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE));
    167                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
    168                 if (streamType == AudioManager.STREAM_VOICE_CALL) {
    169                     int streamValue = intent
    170                             .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
    171                     int hfVol = HeadsetClientStateMachine.amToHfVol(streamValue);
    172                     Log.d(TAG, "Setting volume to audio manager: " + streamValue + " hands free: "
    173                                     + hfVol);
    174                     mAudioManager.setParameters("hfp_volume=" + hfVol);
    175                 }
    176             }
    177         }
    178     };
    179 
    180     /**
    181      * Handlers for incoming service calls
    182      */
    183     private static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
    184             implements IProfileServiceBinder {
    185         private HeadsetClientService mService;
    186 
    187         public BluetoothHeadsetClientBinder(HeadsetClientService svc) {
    188             mService = svc;
    189         }
    190 
    191         @Override
    192         public boolean cleanup() {
    193             mService = null;
    194             return true;
    195         }
    196 
    197         private HeadsetClientService getService() {
    198             if (!Utils.checkCaller()) {
    199                 Log.w(TAG, "HeadsetClient call not allowed for non-active user");
    200                 return null;
    201             }
    202 
    203             if (mService != null && mService.isAvailable()) {
    204                 return mService;
    205             }
    206 
    207             Log.e(TAG, "HeadsetClientService is not available.");
    208             return null;
    209         }
    210 
    211         @Override
    212         public boolean connect(BluetoothDevice device) {
    213             HeadsetClientService service = getService();
    214             if (service == null) {
    215                 return false;
    216             }
    217             return service.connect(device);
    218         }
    219 
    220         @Override
    221         public boolean disconnect(BluetoothDevice device) {
    222             HeadsetClientService service = getService();
    223             if (service == null) {
    224                 return false;
    225             }
    226             return service.disconnect(device);
    227         }
    228 
    229         @Override
    230         public List<BluetoothDevice> getConnectedDevices() {
    231             HeadsetClientService service = getService();
    232             if (service == null) {
    233                 return new ArrayList<BluetoothDevice>(0);
    234             }
    235             return service.getConnectedDevices();
    236         }
    237 
    238         @Override
    239         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    240             HeadsetClientService service = getService();
    241             if (service == null) {
    242                 return new ArrayList<BluetoothDevice>(0);
    243             }
    244             return service.getDevicesMatchingConnectionStates(states);
    245         }
    246 
    247         @Override
    248         public int getConnectionState(BluetoothDevice device) {
    249             HeadsetClientService service = getService();
    250             if (service == null) {
    251                 return BluetoothProfile.STATE_DISCONNECTED;
    252             }
    253             return service.getConnectionState(device);
    254         }
    255 
    256         @Override
    257         public boolean setPriority(BluetoothDevice device, int priority) {
    258             HeadsetClientService service = getService();
    259             if (service == null) {
    260                 return false;
    261             }
    262             return service.setPriority(device, priority);
    263         }
    264 
    265         @Override
    266         public int getPriority(BluetoothDevice device) {
    267             HeadsetClientService service = getService();
    268             if (service == null) {
    269                 return BluetoothProfile.PRIORITY_UNDEFINED;
    270             }
    271             return service.getPriority(device);
    272         }
    273 
    274         @Override
    275         public boolean startVoiceRecognition(BluetoothDevice device) {
    276             HeadsetClientService service = getService();
    277             if (service == null) {
    278                 return false;
    279             }
    280             return service.startVoiceRecognition(device);
    281         }
    282 
    283         @Override
    284         public boolean stopVoiceRecognition(BluetoothDevice device) {
    285             HeadsetClientService service = getService();
    286             if (service == null) {
    287                 return false;
    288             }
    289             return service.stopVoiceRecognition(device);
    290         }
    291 
    292         @Override
    293         public int getAudioState(BluetoothDevice device) {
    294             HeadsetClientService service = getService();
    295             if (service == null) {
    296                 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
    297             }
    298             return service.getAudioState(device);
    299         }
    300 
    301         @Override
    302         public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
    303             Log.e(TAG, "setAudioRouteAllowed API not supported");
    304         }
    305 
    306         @Override
    307         public boolean getAudioRouteAllowed(BluetoothDevice device) {
    308             Log.e(TAG, "getAudioRouteAllowed API not supported");
    309             return false;
    310         }
    311 
    312         @Override
    313         public boolean connectAudio(BluetoothDevice device) {
    314             HeadsetClientService service = getService();
    315             if (service == null) {
    316                 return false;
    317             }
    318             return service.connectAudio(device);
    319         }
    320 
    321         @Override
    322         public boolean disconnectAudio(BluetoothDevice device) {
    323             HeadsetClientService service = getService();
    324             if (service == null) {
    325                 return false;
    326             }
    327             return service.disconnectAudio(device);
    328         }
    329 
    330         @Override
    331         public boolean acceptCall(BluetoothDevice device, int flag) {
    332             HeadsetClientService service = getService();
    333             if (service == null) {
    334                 return false;
    335             }
    336             return service.acceptCall(device, flag);
    337         }
    338 
    339         @Override
    340         public boolean rejectCall(BluetoothDevice device) {
    341             HeadsetClientService service = getService();
    342             if (service == null) {
    343                 return false;
    344             }
    345             return service.rejectCall(device);
    346         }
    347 
    348         @Override
    349         public boolean holdCall(BluetoothDevice device) {
    350             HeadsetClientService service = getService();
    351             if (service == null) {
    352                 return false;
    353             }
    354             return service.holdCall(device);
    355         }
    356 
    357         @Override
    358         public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
    359             HeadsetClientService service = getService();
    360             if (service == null) {
    361                 Log.w(TAG, "service is null");
    362                 return false;
    363             }
    364             return service.terminateCall(device, call != null ? call.getUUID() : null);
    365         }
    366 
    367         @Override
    368         public boolean explicitCallTransfer(BluetoothDevice device) {
    369             HeadsetClientService service = getService();
    370             if (service == null) {
    371                 return false;
    372             }
    373             return service.explicitCallTransfer(device);
    374         }
    375 
    376         @Override
    377         public boolean enterPrivateMode(BluetoothDevice device, int index) {
    378             HeadsetClientService service = getService();
    379             if (service == null) {
    380                 return false;
    381             }
    382             return service.enterPrivateMode(device, index);
    383         }
    384 
    385         @Override
    386         public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
    387             HeadsetClientService service = getService();
    388             if (service == null) {
    389                 return null;
    390             }
    391             return service.dial(device, number);
    392         }
    393 
    394         @Override
    395         public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
    396             HeadsetClientService service = getService();
    397             if (service == null) {
    398                 return new ArrayList<BluetoothHeadsetClientCall>();
    399             }
    400             return service.getCurrentCalls(device);
    401         }
    402 
    403         @Override
    404         public boolean sendDTMF(BluetoothDevice device, byte code) {
    405             HeadsetClientService service = getService();
    406             if (service == null) {
    407                 return false;
    408             }
    409             return service.sendDTMF(device, code);
    410         }
    411 
    412         @Override
    413         public boolean getLastVoiceTagNumber(BluetoothDevice device) {
    414             HeadsetClientService service = getService();
    415             if (service == null) {
    416                 return false;
    417             }
    418             return service.getLastVoiceTagNumber(device);
    419         }
    420 
    421         @Override
    422         public Bundle getCurrentAgEvents(BluetoothDevice device) {
    423             HeadsetClientService service = getService();
    424             if (service == null) {
    425                 return null;
    426             }
    427             return service.getCurrentAgEvents(device);
    428         }
    429 
    430         @Override
    431         public Bundle getCurrentAgFeatures(BluetoothDevice device) {
    432             HeadsetClientService service = getService();
    433             if (service == null) {
    434                 return null;
    435             }
    436             return service.getCurrentAgFeatures(device);
    437         }
    438     };
    439 
    440     // API methods
    441     public static synchronized HeadsetClientService getHeadsetClientService() {
    442         if (sHeadsetClientService != null && sHeadsetClientService.isAvailable()) {
    443             if (DBG) {
    444                 Log.d(TAG, "getHeadsetClientService(): returning " + sHeadsetClientService);
    445             }
    446             return sHeadsetClientService;
    447         }
    448         if (DBG) {
    449             if (sHeadsetClientService == null) {
    450                 Log.d(TAG, "getHeadsetClientService(): service is NULL");
    451             } else if (!(sHeadsetClientService.isAvailable())) {
    452                 Log.d(TAG, "getHeadsetClientService(): service is not available");
    453             }
    454         }
    455         return null;
    456     }
    457 
    458     private static synchronized void setHeadsetClientService(HeadsetClientService instance) {
    459         if (instance != null && instance.isAvailable()) {
    460             if (DBG) {
    461                 Log.d(TAG, "setHeadsetClientService(): set to: " + sHeadsetClientService);
    462             }
    463             sHeadsetClientService = instance;
    464         } else {
    465             if (DBG) {
    466                 if (sHeadsetClientService == null) {
    467                     Log.d(TAG, "setHeadsetClientService(): service not available");
    468                 } else if (!sHeadsetClientService.isAvailable()) {
    469                     Log.d(TAG, "setHeadsetClientService(): service is cleaning up");
    470                 }
    471             }
    472         }
    473     }
    474 
    475     private static synchronized void clearHeadsetClientService() {
    476         sHeadsetClientService = null;
    477     }
    478 
    479     public boolean connect(BluetoothDevice device) {
    480         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    481                 "Need BLUETOOTH ADMIN permission");
    482         if (DBG) {
    483             Log.d(TAG, "connect " + device);
    484         }
    485         HeadsetClientStateMachine sm = getStateMachine(device);
    486         if (sm == null) {
    487             Log.e(TAG, "Cannot allocate SM for device " + device);
    488             return false;
    489         }
    490 
    491         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
    492             Log.w(TAG, "Connection not allowed: <" + device.getAddress() + "> is PRIORITY_OFF");
    493             return false;
    494         }
    495 
    496         sm.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         HeadsetClientStateMachine sm = getStateMachine(device);
    504         if (sm == null) {
    505             Log.e(TAG, "Cannot allocate SM for device " + device);
    506             return false;
    507         }
    508 
    509         int connectionState = sm.getConnectionState(device);
    510         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    511                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    512             return false;
    513         }
    514 
    515         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
    516         return true;
    517     }
    518 
    519     public synchronized List<BluetoothDevice> getConnectedDevices() {
    520         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    521 
    522         ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>();
    523         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
    524             HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
    525             if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) {
    526                 connectedDevices.add(bd);
    527             }
    528         }
    529         return connectedDevices;
    530     }
    531 
    532     private synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    533         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    534         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
    535         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
    536             for (int state : states) {
    537                 HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
    538                 if (sm != null && sm.getConnectionState(bd) == state) {
    539                     devices.add(bd);
    540                 }
    541             }
    542         }
    543         return devices;
    544     }
    545 
    546     private synchronized int getConnectionState(BluetoothDevice device) {
    547         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    548         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
    549         if (sm != null) {
    550             return sm.getConnectionState(device);
    551         }
    552         return BluetoothProfile.STATE_DISCONNECTED;
    553     }
    554 
    555     public boolean setPriority(BluetoothDevice device, int priority) {
    556         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    557                 "Need BLUETOOTH_ADMIN permission");
    558         Settings.Global.putInt(getContentResolver(),
    559                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
    560                 priority);
    561         if (DBG) {
    562             Log.d(TAG, "Saved priority " + device + " = " + priority);
    563         }
    564         return true;
    565     }
    566 
    567     public int getPriority(BluetoothDevice device) {
    568         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    569                 "Need BLUETOOTH_ADMIN permission");
    570         int priority = Settings.Global.getInt(getContentResolver(),
    571                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
    572                 BluetoothProfile.PRIORITY_UNDEFINED);
    573         return priority;
    574     }
    575 
    576     boolean startVoiceRecognition(BluetoothDevice device) {
    577         Log.e(TAG, "startVoiceRecognition API not available");
    578         return false;
    579     }
    580 
    581     boolean stopVoiceRecognition(BluetoothDevice device) {
    582         Log.e(TAG, "stopVoiceRecognition API not available");
    583         return false;
    584     }
    585 
    586     int getAudioState(BluetoothDevice device) {
    587         HeadsetClientStateMachine sm = getStateMachine(device);
    588         if (sm == null) {
    589             Log.e(TAG, "Cannot allocate SM for device " + device);
    590             return -1;
    591         }
    592 
    593         return sm.getAudioState(device);
    594     }
    595 
    596     boolean connectAudio(BluetoothDevice device) {
    597         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    598         HeadsetClientStateMachine sm = getStateMachine(device);
    599         if (sm == null) {
    600             Log.e(TAG, "Cannot allocate SM for device " + device);
    601             return false;
    602         }
    603 
    604         if (!sm.isConnected()) {
    605             return false;
    606         }
    607         if (sm.isAudioOn()) {
    608             return false;
    609         }
    610         sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
    611         return true;
    612     }
    613 
    614     boolean disconnectAudio(BluetoothDevice device) {
    615         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    616         HeadsetClientStateMachine sm = getStateMachine(device);
    617         if (sm == null) {
    618             Log.e(TAG, "Cannot allocate SM for device " + device);
    619             return false;
    620         }
    621 
    622         if (!sm.isAudioOn()) {
    623             return false;
    624         }
    625         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
    626         return true;
    627     }
    628 
    629     boolean holdCall(BluetoothDevice device) {
    630         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    631         HeadsetClientStateMachine sm = getStateMachine(device);
    632         if (sm == null) {
    633             Log.e(TAG, "Cannot allocate SM for device " + device);
    634             return false;
    635         }
    636 
    637         int connectionState = sm.getConnectionState(device);
    638         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    639                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    640             return false;
    641         }
    642         Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
    643         sm.sendMessage(msg);
    644         return true;
    645     }
    646 
    647     boolean acceptCall(BluetoothDevice device, int flag) {
    648         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    649         HeadsetClientStateMachine sm = getStateMachine(device);
    650         if (sm == null) {
    651             Log.e(TAG, "Cannot allocate SM for device " + device);
    652             return false;
    653         }
    654 
    655         int connectionState = sm.getConnectionState(device);
    656         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    657                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    658             return false;
    659         }
    660         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
    661         msg.arg1 = flag;
    662         sm.sendMessage(msg);
    663         return true;
    664     }
    665 
    666     boolean rejectCall(BluetoothDevice device) {
    667         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    668         HeadsetClientStateMachine sm = getStateMachine(device);
    669         if (sm == null) {
    670             Log.e(TAG, "Cannot allocate SM for device " + device);
    671             return false;
    672         }
    673 
    674         int connectionState = sm.getConnectionState(device);
    675         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    676                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    677             return false;
    678         }
    679 
    680         Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
    681         sm.sendMessage(msg);
    682         return true;
    683     }
    684 
    685     boolean terminateCall(BluetoothDevice device, UUID uuid) {
    686         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    687         HeadsetClientStateMachine sm = getStateMachine(device);
    688         if (sm == null) {
    689             Log.e(TAG, "Cannot allocate SM for device " + device);
    690             return false;
    691         }
    692 
    693         int connectionState = sm.getConnectionState(device);
    694         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    695                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    696             return false;
    697         }
    698 
    699         Message msg = sm.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
    700         msg.obj = uuid;
    701         sm.sendMessage(msg);
    702         return true;
    703     }
    704 
    705     boolean enterPrivateMode(BluetoothDevice device, int index) {
    706         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    707         HeadsetClientStateMachine sm = getStateMachine(device);
    708         if (sm == null) {
    709             Log.e(TAG, "Cannot allocate SM for device " + device);
    710             return false;
    711         }
    712 
    713         int connectionState = sm.getConnectionState(device);
    714         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    715                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    716             return false;
    717         }
    718 
    719         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
    720         msg.arg1 = index;
    721         sm.sendMessage(msg);
    722         return true;
    723     }
    724 
    725     BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
    726         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    727         HeadsetClientStateMachine sm = getStateMachine(device);
    728         if (sm == null) {
    729             Log.e(TAG, "Cannot allocate SM for device " + device);
    730             return null;
    731         }
    732 
    733         int connectionState = sm.getConnectionState(device);
    734         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    735                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    736             return null;
    737         }
    738 
    739         BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(
    740             device, HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID,
    741             BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false  /* multiparty */,
    742             true  /* outgoing */);
    743         Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
    744         msg.obj = call;
    745         sm.sendMessage(msg);
    746         return call;
    747     }
    748 
    749     public boolean sendDTMF(BluetoothDevice device, byte code) {
    750         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    751         HeadsetClientStateMachine sm = getStateMachine(device);
    752         if (sm == null) {
    753             Log.e(TAG, "Cannot allocate SM for device " + device);
    754             return false;
    755         }
    756 
    757         int connectionState = sm.getConnectionState(device);
    758         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    759                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    760             return false;
    761         }
    762         Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
    763         msg.arg1 = code;
    764         sm.sendMessage(msg);
    765         return true;
    766     }
    767 
    768     public boolean getLastVoiceTagNumber(BluetoothDevice device) {
    769         return false;
    770     }
    771 
    772     public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
    773         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    774         HeadsetClientStateMachine sm = getStateMachine(device);
    775         if (sm == null) {
    776             Log.e(TAG, "Cannot allocate SM for device " + device);
    777             return null;
    778         }
    779 
    780         int connectionState = sm.getConnectionState(device);
    781         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
    782             return null;
    783         }
    784         return sm.getCurrentCalls();
    785     }
    786 
    787     public boolean explicitCallTransfer(BluetoothDevice device) {
    788         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    789         HeadsetClientStateMachine sm = getStateMachine(device);
    790         if (sm == null) {
    791             Log.e(TAG, "Cannot allocate SM for device " + device);
    792             return false;
    793         }
    794 
    795         int connectionState = sm.getConnectionState(device);
    796         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    797                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    798             return false;
    799         }
    800         Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
    801         sm.sendMessage(msg);
    802         return true;
    803     }
    804 
    805     public Bundle getCurrentAgEvents(BluetoothDevice device) {
    806         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    807         HeadsetClientStateMachine sm = getStateMachine(device);
    808         if (sm == null) {
    809             Log.e(TAG, "Cannot allocate SM for device " + device);
    810             return null;
    811         }
    812 
    813         int connectionState = sm.getConnectionState(device);
    814         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
    815             return null;
    816         }
    817         return sm.getCurrentAgEvents();
    818     }
    819 
    820     public Bundle getCurrentAgFeatures(BluetoothDevice device) {
    821         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    822         HeadsetClientStateMachine sm = getStateMachine(device);
    823         if (sm == null) {
    824             Log.e(TAG, "Cannot allocate SM for device " + device);
    825             return null;
    826         }
    827         int connectionState = sm.getConnectionState(device);
    828         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
    829             return null;
    830         }
    831         return sm.getCurrentAgFeatures();
    832     }
    833 
    834     // Handle messages from native (JNI) to java
    835     public void messageFromNative(StackEvent stackEvent) {
    836         HeadsetClientStateMachine sm = getStateMachine(stackEvent.device);
    837         if (sm == null) {
    838             Log.w(TAG, "No SM found for event " + stackEvent);
    839         }
    840 
    841         sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);
    842     }
    843 
    844     // State machine management
    845     private synchronized HeadsetClientStateMachine getStateMachine(BluetoothDevice device) {
    846         if (device == null) {
    847             Log.e(TAG, "getStateMachine failed: Device cannot be null");
    848             return null;
    849         }
    850 
    851         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
    852         if (sm != null) {
    853             if (DBG) {
    854                 Log.d(TAG, "Found SM for device " + device);
    855             }
    856             return sm;
    857         }
    858 
    859         // There is a possibility of a DOS attack if someone populates here with a lot of fake
    860         // BluetoothAddresses. If it so happens instead of blowing up we can atleast put a limit on
    861         // how long the attack would survive
    862         if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) {
    863             Log.e(TAG, "Max state machines reached, possible DOS attack " +
    864                 MAX_STATE_MACHINES_POSSIBLE);
    865             return null;
    866         }
    867 
    868         // Allocate a new SM
    869         Log.d(TAG, "Creating a new state machine");
    870         sm = mSmFactory.make(this, mSmThread);
    871         mStateMachineMap.put(device, sm);
    872         return sm;
    873     }
    874 
    875     // Check if any of the state machines are currently holding the SCO audio stream
    876     // This function is *only* called from the SMs which are themselves run the same thread and
    877     // hence we do not need synchronization here
    878     boolean isScoAvailable() {
    879         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
    880             HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
    881             int audioState = sm.getAudioState(bd);
    882             if (audioState != BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED) {
    883                 Log.w(TAG, "Device " + bd + " audio state " + audioState + " not disconnected");
    884                 return false;
    885             }
    886         }
    887         return true;
    888     }
    889 
    890     @Override
    891     public synchronized void dump(StringBuilder sb) {
    892         super.dump(sb);
    893         for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
    894             if (sm != null) {
    895                 println(sb, "State machine:");
    896                 println(sb, "=============");
    897                 sm.dump(sb);
    898             }
    899         }
    900     }
    901 
    902     // For testing
    903     protected synchronized Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() {
    904         return mStateMachineMap;
    905     }
    906 
    907     protected void setSMFactory(HeadsetClientStateMachineFactory factory) {
    908         mSmFactory = factory;
    909     }
    910 }
    911