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