Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2011 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 android.bluetooth;
     18 
     19 import android.annotation.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.content.Context;
     22 import android.os.IBinder;
     23 import android.os.RemoteException;
     24 import android.os.ServiceManager;
     25 import android.util.Log;
     26 
     27 import java.util.ArrayList;
     28 import java.util.List;
     29 
     30 
     31 /**
     32  * This class provides the public APIs to control the Bluetooth Input
     33  * Device Profile.
     34  *
     35  *<p>BluetoothInputDevice is a proxy object for controlling the Bluetooth
     36  * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
     37  * the BluetoothInputDevice proxy object.
     38  *
     39  *<p>Each method is protected with its appropriate permission.
     40  *@hide
     41  */
     42 public final class BluetoothInputDevice implements BluetoothProfile {
     43     private static final String TAG = "BluetoothInputDevice";
     44     private static final boolean DBG = false;
     45 
     46     /**
     47      * Intent used to broadcast the change in connection state of the Input
     48      * Device profile.
     49      *
     50      * <p>This intent will have 3 extras:
     51      * <ul>
     52      *   <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
     53      *   <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
     54      *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
     55      * </ul>
     56      *
     57      * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
     58      * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
     59      * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
     60      *
     61      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
     62      * receive.
     63      */
     64     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     65     public static final String ACTION_CONNECTION_STATE_CHANGED =
     66         "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
     67 
     68     /**
     69      * Return codes for the connect and disconnect Bluez / Dbus calls.
     70      * @hide
     71      */
     72     public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000;
     73 
     74     /**
     75      * @hide
     76      */
     77     public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001;
     78 
     79     /**
     80      * @hide
     81      */
     82     public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002;
     83 
     84     /**
     85      * @hide
     86      */
     87     public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003;
     88 
     89     /**
     90      * @hide
     91      */
     92     public static final int INPUT_OPERATION_SUCCESS = 5004;
     93 
     94     private ServiceListener mServiceListener;
     95     private BluetoothAdapter mAdapter;
     96     private IBluetooth mService;
     97 
     98     /**
     99      * Create a BluetoothInputDevice proxy object for interacting with the local
    100      * Bluetooth Service which handles the InputDevice profile
    101      *
    102      */
    103     /*package*/ BluetoothInputDevice(Context mContext, ServiceListener l) {
    104         IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
    105         mServiceListener = l;
    106         mAdapter = BluetoothAdapter.getDefaultAdapter();
    107         if (b != null) {
    108             mService = IBluetooth.Stub.asInterface(b);
    109             if (mServiceListener != null) {
    110                 mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, this);
    111             }
    112         } else {
    113             Log.w(TAG, "Bluetooth Service not available!");
    114 
    115             // Instead of throwing an exception which prevents people from going
    116             // into Wireless settings in the emulator. Let it crash later when it is actually used.
    117             mService = null;
    118         }
    119     }
    120 
    121     /*package*/ void close() {
    122         mServiceListener = null;
    123     }
    124 
    125     /**
    126      * Initiate connection to a profile of the remote bluetooth device.
    127      *
    128      * <p> The system supports connection to multiple input devices.
    129      *
    130      * <p> This API returns false in scenarios like the profile on the
    131      * device is already connected or Bluetooth is not turned on.
    132      * When this API returns true, it is guaranteed that
    133      * connection state intent for the profile will be broadcasted with
    134      * the state. Users can get the connection state of the profile
    135      * from this intent.
    136      *
    137      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    138      * permission.
    139      *
    140      * @param device Remote Bluetooth Device
    141      * @return false on immediate error,
    142      *               true otherwise
    143      * @hide
    144      */
    145     public boolean connect(BluetoothDevice device) {
    146         if (DBG) log("connect(" + device + ")");
    147         if (mService != null && isEnabled() &&
    148             isValidDevice(device)) {
    149             try {
    150                 return mService.connectInputDevice(device);
    151             } catch (RemoteException e) {
    152                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    153                 return false;
    154             }
    155         }
    156         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    157         return false;
    158     }
    159 
    160     /**
    161      * Initiate disconnection from a profile
    162      *
    163      * <p> This API will return false in scenarios like the profile on the
    164      * Bluetooth device is not in connected state etc. When this API returns,
    165      * true, it is guaranteed that the connection state change
    166      * intent will be broadcasted with the state. Users can get the
    167      * disconnection state of the profile from this intent.
    168      *
    169      * <p> If the disconnection is initiated by a remote device, the state
    170      * will transition from {@link #STATE_CONNECTED} to
    171      * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
    172      * host (local) device the state will transition from
    173      * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
    174      * state {@link #STATE_DISCONNECTED}. The transition to
    175      * {@link #STATE_DISCONNECTING} can be used to distinguish between the
    176      * two scenarios.
    177      *
    178      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    179      * permission.
    180      *
    181      * @param device Remote Bluetooth Device
    182      * @return false on immediate error,
    183      *               true otherwise
    184      * @hide
    185      */
    186     public boolean disconnect(BluetoothDevice device) {
    187         if (DBG) log("disconnect(" + device + ")");
    188         if (mService != null && isEnabled() &&
    189             isValidDevice(device)) {
    190             try {
    191                 return mService.disconnectInputDevice(device);
    192             } catch (RemoteException e) {
    193                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    194                 return false;
    195             }
    196         }
    197         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    198         return false;
    199     }
    200 
    201     /**
    202      * {@inheritDoc}
    203      */
    204     public List<BluetoothDevice> getConnectedDevices() {
    205         if (DBG) log("getConnectedDevices()");
    206         if (mService != null && isEnabled()) {
    207             try {
    208                 return mService.getConnectedInputDevices();
    209             } catch (RemoteException e) {
    210                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    211                 return new ArrayList<BluetoothDevice>();
    212             }
    213         }
    214         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    215         return new ArrayList<BluetoothDevice>();
    216     }
    217 
    218     /**
    219      * {@inheritDoc}
    220      */
    221     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    222         if (DBG) log("getDevicesMatchingStates()");
    223         if (mService != null && isEnabled()) {
    224             try {
    225                 return mService.getInputDevicesMatchingConnectionStates(states);
    226             } catch (RemoteException e) {
    227                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    228                 return new ArrayList<BluetoothDevice>();
    229             }
    230         }
    231         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    232         return new ArrayList<BluetoothDevice>();
    233     }
    234 
    235     /**
    236      * {@inheritDoc}
    237      */
    238     public int getConnectionState(BluetoothDevice device) {
    239         if (DBG) log("getState(" + device + ")");
    240         if (mService != null && isEnabled()
    241             && isValidDevice(device)) {
    242             try {
    243                 return mService.getInputDeviceConnectionState(device);
    244             } catch (RemoteException e) {
    245                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    246                 return BluetoothProfile.STATE_DISCONNECTED;
    247             }
    248         }
    249         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    250         return BluetoothProfile.STATE_DISCONNECTED;
    251     }
    252 
    253     /**
    254      * Set priority of the profile
    255      *
    256      * <p> The device should already be paired.
    257      *  Priority can be one of {@link #PRIORITY_ON} or
    258      * {@link #PRIORITY_OFF},
    259      *
    260      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    261      * permission.
    262      *
    263      * @param device Paired bluetooth device
    264      * @param priority
    265      * @return true if priority is set, false on error
    266      * @hide
    267      */
    268     public boolean setPriority(BluetoothDevice device, int priority) {
    269         if (DBG) log("setPriority(" + device + ", " + priority + ")");
    270         if (mService != null && isEnabled()
    271             && isValidDevice(device)) {
    272             if (priority != BluetoothProfile.PRIORITY_OFF &&
    273                 priority != BluetoothProfile.PRIORITY_ON) {
    274               return false;
    275             }
    276             try {
    277                 return mService.setInputDevicePriority(device, priority);
    278             } catch (RemoteException e) {
    279                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    280                 return false;
    281             }
    282         }
    283         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    284         return false;
    285     }
    286 
    287     /**
    288      * Get the priority of the profile.
    289      *
    290      * <p> The priority can be any of:
    291      * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
    292      * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
    293      *
    294      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    295      *
    296      * @param device Bluetooth device
    297      * @return priority of the device
    298      * @hide
    299      */
    300     public int getPriority(BluetoothDevice device) {
    301         if (DBG) log("getPriority(" + device + ")");
    302         if (mService != null && isEnabled()
    303             && isValidDevice(device)) {
    304             try {
    305                 return mService.getInputDevicePriority(device);
    306             } catch (RemoteException e) {
    307                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    308                 return BluetoothProfile.PRIORITY_OFF;
    309             }
    310         }
    311         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    312         return BluetoothProfile.PRIORITY_OFF;
    313     }
    314 
    315     private boolean isEnabled() {
    316        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
    317        return false;
    318     }
    319 
    320     private boolean isValidDevice(BluetoothDevice device) {
    321        if (device == null) return false;
    322 
    323        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
    324        return false;
    325     }
    326 
    327     private static void log(String msg) {
    328       Log.d(TAG, msg);
    329     }
    330 }
    331