Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2017 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.googlecode.android_scripting.facade.bluetooth;
     18 
     19 import android.app.Service;
     20 import android.bluetooth.BluetoothAdapter;
     21 import android.bluetooth.BluetoothDevice;
     22 import android.bluetooth.BluetoothHeadset;
     23 import android.bluetooth.BluetoothProfile;
     24 import android.bluetooth.BluetoothUuid;
     25 import android.os.ParcelUuid;
     26 
     27 import com.googlecode.android_scripting.Log;
     28 import com.googlecode.android_scripting.facade.FacadeManager;
     29 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
     30 import com.googlecode.android_scripting.rpc.Rpc;
     31 import com.googlecode.android_scripting.rpc.RpcParameter;
     32 
     33 import java.util.List;
     34 
     35 public class BluetoothHspFacade extends RpcReceiver {
     36     static final ParcelUuid[] UUIDS = {
     37             BluetoothUuid.HSP, BluetoothUuid.Handsfree
     38     };
     39 
     40     private final Service mService;
     41     private final BluetoothAdapter mBluetoothAdapter;
     42 
     43     private static boolean sIsHspReady = false;
     44     private static BluetoothHeadset sHspProfile = null;
     45 
     46     public BluetoothHspFacade(FacadeManager manager) {
     47         super(manager);
     48         mService = manager.getService();
     49         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     50         mBluetoothAdapter.getProfileProxy(mService, new HspServiceListener(),
     51                 BluetoothProfile.HEADSET);
     52     }
     53 
     54     class HspServiceListener implements BluetoothProfile.ServiceListener {
     55         @Override
     56         public void onServiceConnected(int profile, BluetoothProfile proxy) {
     57             sHspProfile = (BluetoothHeadset) proxy;
     58             sIsHspReady = true;
     59         }
     60 
     61         @Override
     62         public void onServiceDisconnected(int profile) {
     63             sIsHspReady = false;
     64         }
     65     }
     66 
     67     /**
     68      * Connect to Hsp Profile
     69      * @param device - the BluetoothDevice object to connect to.
     70      * @return if the connection was successfull or not.
     71      */
     72     public Boolean hspConnect(BluetoothDevice device) {
     73         if (sHspProfile == null) return false;
     74         return sHspProfile.connect(device);
     75     }
     76 
     77     /**
     78      * Disconnect to Hsp Profile.
     79      * @param device - the Bluetooth Device object to disconnect from.
     80      * @return if the disconnection was successfull or not.
     81      */
     82     public Boolean hspDisconnect(BluetoothDevice device) {
     83         if (sHspProfile == null) return false;
     84         return sHspProfile.disconnect(device);
     85     }
     86 
     87     /**
     88      * Wait Hsp Profile ready until timeout.
     89      * @param timeout - Timeout second.
     90      * @return true if Hsp Profile is ready else false.
     91      */
     92     public Boolean waitHspReady(long timeout) {
     93         long startTime = System.currentTimeMillis();
     94         while (System.currentTimeMillis() < startTime + timeout * 1000) {
     95             if (sIsHspReady) return true;
     96         }
     97         Log.d("Hsp profile is not ready");
     98         return false;
     99     }
    100 
    101     /**
    102      * Is Hsp profile ready.
    103      * @return if Hsp profile is ready or not.
    104      */
    105     @Rpc(description = "Is Hsp profile ready.")
    106     public Boolean bluetoothHspIsReady() {
    107         return sIsHspReady;
    108     }
    109 
    110     /**
    111      * Set priority of the profile.
    112      * @param deviceStr - name or MAC address of a Bluetooth device.
    113      * @param priority - Priority that needs to be set.
    114      */
    115     @Rpc(description = "Set priority of the profile.")
    116     public void bluetoothHspSetPriority(
    117             @RpcParameter(name = "device", description = "Mac address of a BT device.")
    118                 String deviceStr,
    119             @RpcParameter(name = "priority", description = "Priority that needs to be set.")
    120                 Integer priority) throws Exception {
    121         if (!waitHspReady(10)) return;
    122         BluetoothDevice device = BluetoothFacade.getDevice(
    123                 mBluetoothAdapter.getBondedDevices(), deviceStr);
    124         Log.d("Changing priority of device " + device.getAliasName() + " p: " + priority);
    125         sHspProfile.setPriority(device, priority);
    126     }
    127 
    128     /**
    129      * Connect to an HSP device.
    130      * @param device - Name or MAC address of a bluetooth device.
    131      * @return True if the connection was successful; otherwise False.
    132      */
    133     @Rpc(description = "Connect to an HSP device.")
    134     public Boolean bluetoothHspConnect(
    135             @RpcParameter(name = "device", description =
    136                 "Name or MAC address of a bluetooth device.")
    137                 String device) throws Exception {
    138         if (!waitHspReady(10)) return false;
    139         BluetoothDevice mDevice = BluetoothFacade.getDevice(
    140                 mBluetoothAdapter.getBondedDevices(), device);
    141         Log.d("Connecting to device " + mDevice.getAliasName());
    142         return hspConnect(mDevice);
    143     }
    144 
    145     /**
    146      * Disconnect an HSP device.
    147      * @param device - Name or MAC address of a bluetooth device.
    148      * @return True if the disconnection was successful; otherwise False.
    149      */
    150     @Rpc(description = "Disconnect an HSP device.")
    151     public Boolean bluetoothHspDisconnect(
    152             @RpcParameter(name = "device", description = "Name or MAC address of a device.")
    153                 String device) throws Exception {
    154         if (!waitHspReady(10)) return false;
    155         Log.d("Connected devices: " + sHspProfile.getConnectedDevices());
    156         BluetoothDevice mDevice = BluetoothFacade.getDevice(
    157                 sHspProfile.getConnectedDevices(), device);
    158         return hspDisconnect(mDevice);
    159     }
    160 
    161      /**
    162      * Get all the devices connected through HSP.
    163      * @return List of all the devices connected through HSP.
    164      */
    165     @Rpc(description = "Get all the devices connected through HSP.")
    166     public List<BluetoothDevice> bluetoothHspGetConnectedDevices() {
    167         if (!waitHspReady(10)) return null;
    168         return sHspProfile.getConnectedDevices();
    169     }
    170 
    171     /**
    172      * Get the connection status of a device.
    173      * @param deviceID - Name or MAC address of a bluetooth device.
    174      * @return connection status of a device.
    175      */
    176     @Rpc(description = "Get the connection status of a device.")
    177     public Integer bluetoothHspGetConnectionStatus(
    178             @RpcParameter(name = "deviceID",
    179                 description = "Name or MAC address of a bluetooth device.")
    180                     String deviceID) {
    181         if (!waitHspReady(10)) {
    182             return BluetoothProfile.STATE_DISCONNECTED;
    183         }
    184         List<BluetoothDevice> deviceList = sHspProfile.getConnectedDevices();
    185         BluetoothDevice device;
    186         try {
    187             device = BluetoothFacade.getDevice(deviceList, deviceID);
    188         } catch (Exception e) {
    189             return BluetoothProfile.STATE_DISCONNECTED;
    190         }
    191         return sHspProfile.getConnectionState(device);
    192     }
    193 
    194     /**
    195      * Force SCO audio on DUT, ignore all other restrictions
    196      *
    197      * @param force True to force SCO audio, False to resume normal
    198      * @return True if the setup is successful
    199      */
    200     @Rpc(description = "Force SCO audio connection on DUT.")
    201     public Boolean bluetoothHspForceScoAudio(
    202             @RpcParameter(name = "force", description = "whether to force SCO audio")
    203                 Boolean force) {
    204         if (!waitHspReady(10)) {
    205             return false;
    206         }
    207         sHspProfile.setForceScoAudio(force);
    208         return true;
    209     }
    210 
    211     /**
    212      * Connect SCO audio to a remote device
    213      *
    214      * @param deviceAddress the Bluetooth MAC address of remote device
    215      * @return True if connection is successful, False otherwise
    216      */
    217     @Rpc(description = "Connect SCO audio for a remote device.")
    218     public Boolean bluetoothHspConnectAudio(
    219             @RpcParameter(name = "deviceAddress",
    220                 description = "MAC address of a bluetooth device.")
    221                     String deviceAddress) {
    222         if (!waitHspReady(10)) {
    223             return false;
    224         }
    225         Log.d("Connected devices: " + sHspProfile.getConnectedDevices());
    226         BluetoothDevice device = null;
    227         if (sHspProfile.getConnectedDevices().size() > 1) {
    228             Log.d("More than one device available");
    229         }
    230         try {
    231             device = BluetoothFacade.getDevice(
    232                     sHspProfile.getConnectedDevices(), deviceAddress);
    233         } catch (Exception e) {
    234             Log.d("Cannot find device " + deviceAddress);
    235             return false;
    236         }
    237         return sHspProfile.connectAudio();
    238     }
    239 
    240     /**
    241      * Disconnect SCO audio for a remote device
    242      *
    243      * @param deviceAddress the Bluetooth MAC address of remote device
    244      * @return True if disconnection is successful, False otherwise
    245      */
    246     @Rpc(description = "Disconnect SCO audio for a remote device")
    247     public Boolean bluetoothHspDisconnectAudio(
    248             @RpcParameter(name = "deviceAddress",
    249                 description = "MAC address of a bluetooth device.")
    250                     String deviceAddress) {
    251         if (!waitHspReady(10)) {
    252             return false;
    253         }
    254         Log.d("Connected devices: " + sHspProfile.getConnectedDevices());
    255         BluetoothDevice device = null;
    256         if (sHspProfile.getConnectedDevices().size() > 1) {
    257             Log.d("More than one device available");
    258         }
    259         try {
    260             device = BluetoothFacade.getDevice(
    261                     sHspProfile.getConnectedDevices(), deviceAddress);
    262         } catch (Exception e) {
    263             Log.d("Cannot find device " + deviceAddress);
    264             return false;
    265         }
    266         if (!sHspProfile.isAudioConnected(device)) {
    267             Log.d("SCO audio is not connected for device " + deviceAddress);
    268             return false;
    269         }
    270         return sHspProfile.disconnectAudio();
    271     }
    272 
    273     /**
    274      * Check if SCO audio is connected for a remote device
    275      *
    276      * @param deviceAddress the Bluetooth MAC address of remote device
    277      * @return True if device is connected to us via SCO audio, False otherwise
    278      */
    279     @Rpc(description = "Check if SCO audio is connected for a remote device")
    280     public Boolean bluetoothHspIsAudioConnected(
    281             @RpcParameter(name = "deviceAddress",
    282                 description = "MAC address of a bluetooth device.")
    283                     String deviceAddress) {
    284         if (!waitHspReady(10)) {
    285             return false;
    286         }
    287         Log.d("Connected devices: " + sHspProfile.getConnectedDevices());
    288         BluetoothDevice device = null;
    289         if (sHspProfile.getConnectedDevices().size() > 1) {
    290             Log.d("More than one device available");
    291         }
    292         try {
    293             device = BluetoothFacade.getDevice(
    294                     sHspProfile.getConnectedDevices(), deviceAddress);
    295         } catch (Exception e) {
    296             Log.d("Cannot find device " + deviceAddress);
    297             return false;
    298         }
    299         return sHspProfile.isAudioConnected(device);
    300     }
    301 
    302     /**
    303      * Start voice recognition. Send BVRA command.
    304      *
    305      * @param deviceAddress the Bluetooth MAC address of remote device
    306      * @return True if started successfully, False otherwise.
    307      */
    308     @Rpc(description = "Start Voice Recognition.")
    309     public Boolean bluetoothHspStartVoiceRecognition(
    310             @RpcParameter(name = "deviceAddress",
    311                     description = "MAC address of a bluetooth device.")
    312                         String deviceAddress) throws Exception {
    313         BluetoothDevice device = BluetoothFacade.getDevice(
    314                 sHspProfile.getConnectedDevices(), deviceAddress);
    315         return sHspProfile.startVoiceRecognition(device);
    316     }
    317 
    318     /**
    319      * Stop voice recognition. Send BVRA command.
    320      *
    321      * @param deviceAddress the Bluetooth MAC address of remote device
    322      * @return True if stopped successfully, False otherwise.
    323      */
    324     @Rpc(description = "Stop Voice Recognition.")
    325     public Boolean bluetoothHspStopVoiceRecognition(
    326             @RpcParameter(name = "deviceAddress",
    327                 description = "MAC address of a bluetooth device.")
    328                     String deviceAddress) throws Exception {
    329         BluetoothDevice device = BluetoothFacade.getDevice(
    330                 sHspProfile.getConnectedDevices(), deviceAddress);
    331         return sHspProfile.stopVoiceRecognition(device);
    332     }
    333 
    334     /**
    335      * Determine whether in-band ringtone is enabled or not.
    336      *
    337      * @return True if enabled, False otherwise.
    338      */
    339     @Rpc(description = "In-band ringtone enabled check.")
    340     public Boolean bluetoothHspIsInbandRingingEnabled() {
    341         return sHspProfile.isInbandRingingEnabled();
    342     }
    343 
    344     /**
    345      * Send a CLCC response from Sl4a (experimental).
    346      *
    347      * @param index the index of the call
    348      * @param direction the direction of the call
    349      * @param status the status of the call
    350      * @param mode the mode
    351      * @param mpty the mpty value
    352      * @param number the phone number
    353      * @param type the type
    354      */
    355     @Rpc(description = "Send generic clcc response.")
    356     public void bluetoothHspClccResponse(
    357             @RpcParameter(name = "index", description = "") Integer index,
    358             @RpcParameter(name = "direction", description = "") Integer direction,
    359             @RpcParameter(name = "status", description = "") Integer status,
    360             @RpcParameter(name = "mode", description = "") Integer mode,
    361             @RpcParameter(name = "mpty", description = "") Boolean mpty,
    362             @RpcParameter(name = "number", description = "") String number,
    363             @RpcParameter(name = "type", description = "") Integer type
    364                   ) {
    365         sHspProfile.clccResponse(index, direction, status, mode, mpty, number, type);
    366     }
    367 
    368     @Override
    369     public void shutdown() {
    370     }
    371 }
    372