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                 if (DBG) {
    166                     Log.d(TAG,
    167                             "Volume changed for stream: "
    168                                     + intent.getExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE));
    169                 }
    170                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
    171                 if (streamType == AudioManager.STREAM_VOICE_CALL) {
    172                     int streamValue = intent
    173                             .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
    174                     int hfVol = HeadsetClientStateMachine.amToHfVol(streamValue);
    175                     if (DBG) {
    176                         Log.d(TAG,
    177                                 "Setting volume to audio manager: " + streamValue
    178                                         + " hands free: " + hfVol);
    179                     }
    180                     mAudioManager.setParameters("hfp_volume=" + hfVol);
    181                     synchronized (this) {
    182                         for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
    183                             if (sm != null) {
    184                                 sm.sendMessage(
    185                                         HeadsetClientStateMachine.SET_SPEAKER_VOLUME, streamValue);
    186                             }
    187                         }
    188                     }
    189                 }
    190             }
    191         }
    192     };
    193 
    194     /**
    195      * Handlers for incoming service calls
    196      */
    197     private static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
    198             implements IProfileServiceBinder {
    199         private HeadsetClientService mService;
    200 
    201         public BluetoothHeadsetClientBinder(HeadsetClientService svc) {
    202             mService = svc;
    203         }
    204 
    205         @Override
    206         public boolean cleanup() {
    207             mService = null;
    208             return true;
    209         }
    210 
    211         private HeadsetClientService getService() {
    212             if (!Utils.checkCaller()) {
    213                 Log.w(TAG, "HeadsetClient call not allowed for non-active user");
    214                 return null;
    215             }
    216 
    217             if (mService != null && mService.isAvailable()) {
    218                 return mService;
    219             }
    220 
    221             Log.e(TAG, "HeadsetClientService is not available.");
    222             return null;
    223         }
    224 
    225         @Override
    226         public boolean connect(BluetoothDevice device) {
    227             HeadsetClientService service = getService();
    228             if (service == null) {
    229                 return false;
    230             }
    231             return service.connect(device);
    232         }
    233 
    234         @Override
    235         public boolean disconnect(BluetoothDevice device) {
    236             HeadsetClientService service = getService();
    237             if (service == null) {
    238                 return false;
    239             }
    240             return service.disconnect(device);
    241         }
    242 
    243         @Override
    244         public List<BluetoothDevice> getConnectedDevices() {
    245             HeadsetClientService service = getService();
    246             if (service == null) {
    247                 return new ArrayList<BluetoothDevice>(0);
    248             }
    249             return service.getConnectedDevices();
    250         }
    251 
    252         @Override
    253         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    254             HeadsetClientService service = getService();
    255             if (service == null) {
    256                 return new ArrayList<BluetoothDevice>(0);
    257             }
    258             return service.getDevicesMatchingConnectionStates(states);
    259         }
    260 
    261         @Override
    262         public int getConnectionState(BluetoothDevice device) {
    263             HeadsetClientService service = getService();
    264             if (service == null) {
    265                 return BluetoothProfile.STATE_DISCONNECTED;
    266             }
    267             return service.getConnectionState(device);
    268         }
    269 
    270         @Override
    271         public boolean setPriority(BluetoothDevice device, int priority) {
    272             HeadsetClientService service = getService();
    273             if (service == null) {
    274                 return false;
    275             }
    276             return service.setPriority(device, priority);
    277         }
    278 
    279         @Override
    280         public int getPriority(BluetoothDevice device) {
    281             HeadsetClientService service = getService();
    282             if (service == null) {
    283                 return BluetoothProfile.PRIORITY_UNDEFINED;
    284             }
    285             return service.getPriority(device);
    286         }
    287 
    288         @Override
    289         public boolean startVoiceRecognition(BluetoothDevice device) {
    290             HeadsetClientService service = getService();
    291             if (service == null) {
    292                 return false;
    293             }
    294             return service.startVoiceRecognition(device);
    295         }
    296 
    297         @Override
    298         public boolean stopVoiceRecognition(BluetoothDevice device) {
    299             HeadsetClientService service = getService();
    300             if (service == null) {
    301                 return false;
    302             }
    303             return service.stopVoiceRecognition(device);
    304         }
    305 
    306         @Override
    307         public int getAudioState(BluetoothDevice device) {
    308             HeadsetClientService service = getService();
    309             if (service == null) {
    310                 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
    311             }
    312             return service.getAudioState(device);
    313         }
    314 
    315         @Override
    316         public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
    317             Log.e(TAG, "setAudioRouteAllowed API not supported");
    318         }
    319 
    320         @Override
    321         public boolean getAudioRouteAllowed(BluetoothDevice device) {
    322             Log.e(TAG, "getAudioRouteAllowed API not supported");
    323             return false;
    324         }
    325 
    326         @Override
    327         public boolean connectAudio(BluetoothDevice device) {
    328             HeadsetClientService service = getService();
    329             if (service == null) {
    330                 return false;
    331             }
    332             return service.connectAudio(device);
    333         }
    334 
    335         @Override
    336         public boolean disconnectAudio(BluetoothDevice device) {
    337             HeadsetClientService service = getService();
    338             if (service == null) {
    339                 return false;
    340             }
    341             return service.disconnectAudio(device);
    342         }
    343 
    344         @Override
    345         public boolean acceptCall(BluetoothDevice device, int flag) {
    346             HeadsetClientService service = getService();
    347             if (service == null) {
    348                 return false;
    349             }
    350             return service.acceptCall(device, flag);
    351         }
    352 
    353         @Override
    354         public boolean rejectCall(BluetoothDevice device) {
    355             HeadsetClientService service = getService();
    356             if (service == null) {
    357                 return false;
    358             }
    359             return service.rejectCall(device);
    360         }
    361 
    362         @Override
    363         public boolean holdCall(BluetoothDevice device) {
    364             HeadsetClientService service = getService();
    365             if (service == null) {
    366                 return false;
    367             }
    368             return service.holdCall(device);
    369         }
    370 
    371         @Override
    372         public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
    373             HeadsetClientService service = getService();
    374             if (service == null) {
    375                 Log.w(TAG, "service is null");
    376                 return false;
    377             }
    378             return service.terminateCall(device, call != null ? call.getUUID() : null);
    379         }
    380 
    381         @Override
    382         public boolean explicitCallTransfer(BluetoothDevice device) {
    383             HeadsetClientService service = getService();
    384             if (service == null) {
    385                 return false;
    386             }
    387             return service.explicitCallTransfer(device);
    388         }
    389 
    390         @Override
    391         public boolean enterPrivateMode(BluetoothDevice device, int index) {
    392             HeadsetClientService service = getService();
    393             if (service == null) {
    394                 return false;
    395             }
    396             return service.enterPrivateMode(device, index);
    397         }
    398 
    399         @Override
    400         public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
    401             HeadsetClientService service = getService();
    402             if (service == null) {
    403                 return null;
    404             }
    405             return service.dial(device, number);
    406         }
    407 
    408         @Override
    409         public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
    410             HeadsetClientService service = getService();
    411             if (service == null) {
    412                 return new ArrayList<BluetoothHeadsetClientCall>();
    413             }
    414             return service.getCurrentCalls(device);
    415         }
    416 
    417         @Override
    418         public boolean sendDTMF(BluetoothDevice device, byte code) {
    419             HeadsetClientService service = getService();
    420             if (service == null) {
    421                 return false;
    422             }
    423             return service.sendDTMF(device, code);
    424         }
    425 
    426         @Override
    427         public boolean getLastVoiceTagNumber(BluetoothDevice device) {
    428             HeadsetClientService service = getService();
    429             if (service == null) {
    430                 return false;
    431             }
    432             return service.getLastVoiceTagNumber(device);
    433         }
    434 
    435         @Override
    436         public Bundle getCurrentAgEvents(BluetoothDevice device) {
    437             HeadsetClientService service = getService();
    438             if (service == null) {
    439                 return null;
    440             }
    441             return service.getCurrentAgEvents(device);
    442         }
    443 
    444         @Override
    445         public Bundle getCurrentAgFeatures(BluetoothDevice device) {
    446             HeadsetClientService service = getService();
    447             if (service == null) {
    448                 return null;
    449             }
    450             return service.getCurrentAgFeatures(device);
    451         }
    452     };
    453 
    454     // API methods
    455     public static synchronized HeadsetClientService getHeadsetClientService() {
    456         if (sHeadsetClientService != null && sHeadsetClientService.isAvailable()) {
    457             if (DBG) {
    458                 Log.d(TAG, "getHeadsetClientService(): returning " + sHeadsetClientService);
    459             }
    460             return sHeadsetClientService;
    461         }
    462         if (DBG) {
    463             if (sHeadsetClientService == null) {
    464                 Log.d(TAG, "getHeadsetClientService(): service is NULL");
    465             } else if (!(sHeadsetClientService.isAvailable())) {
    466                 Log.d(TAG, "getHeadsetClientService(): service is not available");
    467             }
    468         }
    469         return null;
    470     }
    471 
    472     private static synchronized void setHeadsetClientService(HeadsetClientService instance) {
    473         if (instance != null && instance.isAvailable()) {
    474             if (DBG) {
    475                 Log.d(TAG, "setHeadsetClientService(): set to: " + sHeadsetClientService);
    476             }
    477             sHeadsetClientService = instance;
    478         } else {
    479             if (DBG) {
    480                 if (sHeadsetClientService == null) {
    481                     Log.d(TAG, "setHeadsetClientService(): service not available");
    482                 } else if (!sHeadsetClientService.isAvailable()) {
    483                     Log.d(TAG, "setHeadsetClientService(): service is cleaning up");
    484                 }
    485             }
    486         }
    487     }
    488 
    489     private static synchronized void clearHeadsetClientService() {
    490         sHeadsetClientService = null;
    491     }
    492 
    493     public boolean connect(BluetoothDevice device) {
    494         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    495                 "Need BLUETOOTH ADMIN permission");
    496         if (DBG) {
    497             Log.d(TAG, "connect " + device);
    498         }
    499         HeadsetClientStateMachine sm = getStateMachine(device);
    500         if (sm == null) {
    501             Log.e(TAG, "Cannot allocate SM for device " + device);
    502             return false;
    503         }
    504 
    505         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
    506             Log.w(TAG, "Connection not allowed: <" + device.getAddress() + "> is PRIORITY_OFF");
    507             return false;
    508         }
    509 
    510         sm.sendMessage(HeadsetClientStateMachine.CONNECT, device);
    511         return true;
    512     }
    513 
    514     boolean disconnect(BluetoothDevice device) {
    515         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    516                 "Need BLUETOOTH ADMIN permission");
    517         HeadsetClientStateMachine sm = getStateMachine(device);
    518         if (sm == null) {
    519             Log.e(TAG, "Cannot allocate SM for device " + device);
    520             return false;
    521         }
    522 
    523         int connectionState = sm.getConnectionState(device);
    524         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    525                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    526             return false;
    527         }
    528 
    529         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
    530         return true;
    531     }
    532 
    533     public synchronized List<BluetoothDevice> getConnectedDevices() {
    534         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    535 
    536         ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>();
    537         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
    538             HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
    539             if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) {
    540                 connectedDevices.add(bd);
    541             }
    542         }
    543         return connectedDevices;
    544     }
    545 
    546     private synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    547         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    548         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
    549         for (BluetoothDevice bd : mStateMachineMap.keySet()) {
    550             for (int state : states) {
    551                 HeadsetClientStateMachine sm = mStateMachineMap.get(bd);
    552                 if (sm != null && sm.getConnectionState(bd) == state) {
    553                     devices.add(bd);
    554                 }
    555             }
    556         }
    557         return devices;
    558     }
    559 
    560     private synchronized int getConnectionState(BluetoothDevice device) {
    561         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    562         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
    563         if (sm != null) {
    564             return sm.getConnectionState(device);
    565         }
    566         return BluetoothProfile.STATE_DISCONNECTED;
    567     }
    568 
    569     public boolean setPriority(BluetoothDevice device, int priority) {
    570         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    571                 "Need BLUETOOTH_ADMIN permission");
    572         Settings.Global.putInt(getContentResolver(),
    573                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
    574                 priority);
    575         if (DBG) {
    576             Log.d(TAG, "Saved priority " + device + " = " + priority);
    577         }
    578         return true;
    579     }
    580 
    581     public int getPriority(BluetoothDevice device) {
    582         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    583                 "Need BLUETOOTH_ADMIN permission");
    584         int priority = Settings.Global.getInt(getContentResolver(),
    585                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
    586                 BluetoothProfile.PRIORITY_UNDEFINED);
    587         return priority;
    588     }
    589 
    590     boolean startVoiceRecognition(BluetoothDevice device) {
    591         Log.e(TAG, "startVoiceRecognition API not available");
    592         return false;
    593     }
    594 
    595     boolean stopVoiceRecognition(BluetoothDevice device) {
    596         Log.e(TAG, "stopVoiceRecognition API not available");
    597         return false;
    598     }
    599 
    600     int getAudioState(BluetoothDevice device) {
    601         HeadsetClientStateMachine sm = getStateMachine(device);
    602         if (sm == null) {
    603             Log.e(TAG, "Cannot allocate SM for device " + device);
    604             return -1;
    605         }
    606 
    607         return sm.getAudioState(device);
    608     }
    609 
    610     boolean connectAudio(BluetoothDevice device) {
    611         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    612         HeadsetClientStateMachine sm = getStateMachine(device);
    613         if (sm == null) {
    614             Log.e(TAG, "Cannot allocate SM for device " + device);
    615             return false;
    616         }
    617 
    618         if (!sm.isConnected()) {
    619             return false;
    620         }
    621         if (sm.isAudioOn()) {
    622             return false;
    623         }
    624         sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
    625         return true;
    626     }
    627 
    628     boolean disconnectAudio(BluetoothDevice device) {
    629         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    630         HeadsetClientStateMachine sm = getStateMachine(device);
    631         if (sm == null) {
    632             Log.e(TAG, "Cannot allocate SM for device " + device);
    633             return false;
    634         }
    635 
    636         if (!sm.isAudioOn()) {
    637             return false;
    638         }
    639         sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
    640         return true;
    641     }
    642 
    643     boolean holdCall(BluetoothDevice device) {
    644         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    645         HeadsetClientStateMachine sm = getStateMachine(device);
    646         if (sm == null) {
    647             Log.e(TAG, "Cannot allocate SM for device " + device);
    648             return false;
    649         }
    650 
    651         int connectionState = sm.getConnectionState(device);
    652         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    653                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    654             return false;
    655         }
    656         Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
    657         sm.sendMessage(msg);
    658         return true;
    659     }
    660 
    661     boolean acceptCall(BluetoothDevice device, int flag) {
    662         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    663         /* Phonecalls from a single device are supported, hang up any calls on the other phone */
    664         synchronized (this) {
    665             for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry :
    666                     mStateMachineMap.entrySet()) {
    667                 if (entry.getValue() == null || entry.getKey().equals(device)) {
    668                     continue;
    669                 }
    670                 int connectionState = entry.getValue().getConnectionState(entry.getKey());
    671                 if (DBG) {
    672                     Log.d(TAG, "Accepting a call on device " + device
    673                                     + ". Possibly disconnecting on " + entry.getValue());
    674                 }
    675                 if (connectionState == BluetoothProfile.STATE_CONNECTED) {
    676                     entry.getValue()
    677                             .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL)
    678                             .sendToTarget();
    679                 }
    680             }
    681         }
    682         HeadsetClientStateMachine sm = getStateMachine(device);
    683         if (sm == null) {
    684             Log.e(TAG, "Cannot allocate SM for device " + device);
    685             return false;
    686         }
    687 
    688         int connectionState = sm.getConnectionState(device);
    689         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
    690             return false;
    691         }
    692         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
    693         msg.arg1 = flag;
    694         sm.sendMessage(msg);
    695         return true;
    696     }
    697 
    698     boolean rejectCall(BluetoothDevice device) {
    699         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    700         HeadsetClientStateMachine sm = getStateMachine(device);
    701         if (sm == null) {
    702             Log.e(TAG, "Cannot allocate SM for device " + device);
    703             return false;
    704         }
    705 
    706         int connectionState = sm.getConnectionState(device);
    707         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    708                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    709             return false;
    710         }
    711 
    712         Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
    713         sm.sendMessage(msg);
    714         return true;
    715     }
    716 
    717     boolean terminateCall(BluetoothDevice device, UUID uuid) {
    718         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    719         HeadsetClientStateMachine sm = getStateMachine(device);
    720         if (sm == null) {
    721             Log.e(TAG, "Cannot allocate SM for device " + device);
    722             return false;
    723         }
    724 
    725         int connectionState = sm.getConnectionState(device);
    726         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    727                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    728             return false;
    729         }
    730 
    731         Message msg = sm.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
    732         msg.obj = uuid;
    733         sm.sendMessage(msg);
    734         return true;
    735     }
    736 
    737     boolean enterPrivateMode(BluetoothDevice device, int index) {
    738         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    739         HeadsetClientStateMachine sm = getStateMachine(device);
    740         if (sm == null) {
    741             Log.e(TAG, "Cannot allocate SM for device " + device);
    742             return false;
    743         }
    744 
    745         int connectionState = sm.getConnectionState(device);
    746         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    747                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    748             return false;
    749         }
    750 
    751         Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
    752         msg.arg1 = index;
    753         sm.sendMessage(msg);
    754         return true;
    755     }
    756 
    757     BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
    758         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    759         HeadsetClientStateMachine sm = getStateMachine(device);
    760         if (sm == null) {
    761             Log.e(TAG, "Cannot allocate SM for device " + device);
    762             return null;
    763         }
    764 
    765         int connectionState = sm.getConnectionState(device);
    766         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    767                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    768             return null;
    769         }
    770 
    771         BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(
    772             device, HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID,
    773             BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false  /* multiparty */,
    774             true  /* outgoing */);
    775         Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
    776         msg.obj = call;
    777         sm.sendMessage(msg);
    778         return call;
    779     }
    780 
    781     public boolean sendDTMF(BluetoothDevice device, byte code) {
    782         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    783         HeadsetClientStateMachine sm = getStateMachine(device);
    784         if (sm == null) {
    785             Log.e(TAG, "Cannot allocate SM for device " + device);
    786             return false;
    787         }
    788 
    789         int connectionState = sm.getConnectionState(device);
    790         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    791                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    792             return false;
    793         }
    794         Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
    795         msg.arg1 = code;
    796         sm.sendMessage(msg);
    797         return true;
    798     }
    799 
    800     public boolean getLastVoiceTagNumber(BluetoothDevice device) {
    801         return false;
    802     }
    803 
    804     public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
    805         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    806         HeadsetClientStateMachine sm = getStateMachine(device);
    807         if (sm == null) {
    808             Log.e(TAG, "Cannot allocate SM for device " + device);
    809             return null;
    810         }
    811 
    812         int connectionState = sm.getConnectionState(device);
    813         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
    814             return null;
    815         }
    816         return sm.getCurrentCalls();
    817     }
    818 
    819     public boolean explicitCallTransfer(BluetoothDevice device) {
    820         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    821         HeadsetClientStateMachine sm = getStateMachine(device);
    822         if (sm == null) {
    823             Log.e(TAG, "Cannot allocate SM for device " + device);
    824             return false;
    825         }
    826 
    827         int connectionState = sm.getConnectionState(device);
    828         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
    829                 connectionState != BluetoothProfile.STATE_CONNECTING) {
    830             return false;
    831         }
    832         Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
    833         sm.sendMessage(msg);
    834         return true;
    835     }
    836 
    837     public Bundle getCurrentAgEvents(BluetoothDevice device) {
    838         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    839         HeadsetClientStateMachine sm = getStateMachine(device);
    840         if (sm == null) {
    841             Log.e(TAG, "Cannot allocate SM for device " + device);
    842             return null;
    843         }
    844 
    845         int connectionState = sm.getConnectionState(device);
    846         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
    847             return null;
    848         }
    849         return sm.getCurrentAgEvents();
    850     }
    851 
    852     public Bundle getCurrentAgFeatures(BluetoothDevice device) {
    853         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    854         HeadsetClientStateMachine sm = getStateMachine(device);
    855         if (sm == null) {
    856             Log.e(TAG, "Cannot allocate SM for device " + device);
    857             return null;
    858         }
    859         int connectionState = sm.getConnectionState(device);
    860         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
    861             return null;
    862         }
    863         return sm.getCurrentAgFeatures();
    864     }
    865 
    866     // Handle messages from native (JNI) to java
    867     public void messageFromNative(StackEvent stackEvent) {
    868         HeadsetClientStateMachine sm = getStateMachine(stackEvent.device);
    869         if (sm == null) {
    870             Log.w(TAG, "No SM found for event " + stackEvent);
    871         }
    872 
    873         sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);
    874     }
    875 
    876     // State machine management
    877     private synchronized HeadsetClientStateMachine getStateMachine(BluetoothDevice device) {
    878         if (device == null) {
    879             Log.e(TAG, "getStateMachine failed: Device cannot be null");
    880             return null;
    881         }
    882 
    883         HeadsetClientStateMachine sm = mStateMachineMap.get(device);
    884         if (sm != null) {
    885             if (DBG) {
    886                 Log.d(TAG, "Found SM for device " + device);
    887             }
    888             return sm;
    889         }
    890 
    891         // There is a possibility of a DOS attack if someone populates here with a lot of fake
    892         // BluetoothAddresses. If it so happens instead of blowing up we can atleast put a limit on
    893         // how long the attack would survive
    894         if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) {
    895             Log.e(TAG, "Max state machines reached, possible DOS attack " +
    896                 MAX_STATE_MACHINES_POSSIBLE);
    897             return null;
    898         }
    899 
    900         // Allocate a new SM
    901         Log.d(TAG, "Creating a new state machine");
    902         sm = mSmFactory.make(this, mSmThread);
    903         mStateMachineMap.put(device, sm);
    904         return sm;
    905     }
    906 
    907     // Check if any of the state machines have routed the SCO audio stream.
    908     synchronized boolean isScoRouted() {
    909         for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry :
    910                 mStateMachineMap.entrySet()) {
    911             if (entry.getValue() != null) {
    912                 int audioState = entry.getValue().getAudioState(entry.getKey());
    913                 if (audioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) {
    914                     if (DBG) {
    915                         Log.d(TAG, "Device " + entry.getKey() + " audio state " + audioState
    916                                         + " Connected");
    917                     }
    918                     return true;
    919                 }
    920             }
    921         }
    922         return false;
    923     }
    924 
    925     @Override
    926     public synchronized void dump(StringBuilder sb) {
    927         super.dump(sb);
    928         for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
    929             if (sm != null) {
    930                 println(sb, "State machine:");
    931                 println(sb, "=============");
    932                 sm.dump(sb);
    933             }
    934         }
    935     }
    936 
    937     // For testing
    938     protected synchronized Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() {
    939         return mStateMachineMap;
    940     }
    941 
    942     protected void setSMFactory(HeadsetClientStateMachineFactory factory) {
    943         mSmFactory = factory;
    944     }
    945 }
    946