Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2008 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.content.ComponentName;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.ServiceConnection;
     23 import android.os.Binder;
     24 import android.os.IBinder;
     25 import android.os.RemoteException;
     26 import android.util.Log;
     27 
     28 import java.util.ArrayList;
     29 import java.util.List;
     30 
     31 /**
     32  * This class provides the APIs to control the Bluetooth SIM
     33  * Access Profile (SAP).
     34  *
     35  * <p>BluetoothSap is a proxy object for controlling the Bluetooth
     36  * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
     37  * the BluetoothSap proxy object.
     38  *
     39  * <p>Each method is protected with its appropriate permission.
     40  *
     41  * @hide
     42  */
     43 public final class BluetoothSap implements BluetoothProfile {
     44 
     45     private static final String TAG = "BluetoothSap";
     46     private static final boolean DBG = true;
     47     private static final boolean VDBG = false;
     48 
     49     /**
     50      * Intent used to broadcast the change in connection state of the profile.
     51      *
     52      * <p>This intent will have 4 extras:
     53      * <ul>
     54      * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
     55      * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
     56      * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
     57      * </ul>
     58      *
     59      * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
     60      * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
     61      * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
     62      *
     63      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
     64      * receive.
     65      *
     66      * @hide
     67      */
     68     public static final String ACTION_CONNECTION_STATE_CHANGED =
     69             "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
     70 
     71     private volatile IBluetoothSap mService;
     72     private final Context mContext;
     73     private ServiceListener mServiceListener;
     74     private BluetoothAdapter mAdapter;
     75 
     76     /**
     77      * There was an error trying to obtain the state.
     78      *
     79      * @hide
     80      */
     81     public static final int STATE_ERROR = -1;
     82 
     83     /**
     84      * Connection state change succceeded.
     85      *
     86      * @hide
     87      */
     88     public static final int RESULT_SUCCESS = 1;
     89 
     90     /**
     91      * Connection canceled before completion.
     92      *
     93      * @hide
     94      */
     95     public static final int RESULT_CANCELED = 2;
     96 
     97     private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
     98             new IBluetoothStateChangeCallback.Stub() {
     99                 public void onBluetoothStateChange(boolean up) {
    100                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
    101                     if (!up) {
    102                         if (VDBG) Log.d(TAG, "Unbinding service...");
    103                         synchronized (mConnection) {
    104                             try {
    105                                 mService = null;
    106                                 mContext.unbindService(mConnection);
    107                             } catch (Exception re) {
    108                                 Log.e(TAG, "", re);
    109                             }
    110                         }
    111                     } else {
    112                         synchronized (mConnection) {
    113                             try {
    114                                 if (mService == null) {
    115                                     if (VDBG) Log.d(TAG, "Binding service...");
    116                                     doBind();
    117                                 }
    118                             } catch (Exception re) {
    119                                 Log.e(TAG, "", re);
    120                             }
    121                         }
    122                     }
    123                 }
    124             };
    125 
    126     /**
    127      * Create a BluetoothSap proxy object.
    128      */
    129     /*package*/ BluetoothSap(Context context, ServiceListener l) {
    130         if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
    131         mContext = context;
    132         mServiceListener = l;
    133         mAdapter = BluetoothAdapter.getDefaultAdapter();
    134         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    135         if (mgr != null) {
    136             try {
    137                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
    138             } catch (RemoteException e) {
    139                 Log.e(TAG, "", e);
    140             }
    141         }
    142         doBind();
    143     }
    144 
    145     boolean doBind() {
    146         Intent intent = new Intent(IBluetoothSap.class.getName());
    147         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
    148         intent.setComponent(comp);
    149         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
    150                 mContext.getUser())) {
    151             Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
    152             return false;
    153         }
    154         return true;
    155     }
    156 
    157     protected void finalize() throws Throwable {
    158         try {
    159             close();
    160         } finally {
    161             super.finalize();
    162         }
    163     }
    164 
    165     /**
    166      * Close the connection to the backing service.
    167      * Other public functions of BluetoothSap will return default error
    168      * results once close() has been called. Multiple invocations of close()
    169      * are ok.
    170      *
    171      * @hide
    172      */
    173     public synchronized void close() {
    174         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    175         if (mgr != null) {
    176             try {
    177                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
    178             } catch (Exception e) {
    179                 Log.e(TAG, "", e);
    180             }
    181         }
    182 
    183         synchronized (mConnection) {
    184             if (mService != null) {
    185                 try {
    186                     mService = null;
    187                     mContext.unbindService(mConnection);
    188                 } catch (Exception re) {
    189                     Log.e(TAG, "", re);
    190                 }
    191             }
    192         }
    193         mServiceListener = null;
    194     }
    195 
    196     /**
    197      * Get the current state of the BluetoothSap service.
    198      *
    199      * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
    200      * connected to the Sap service.
    201      * @hide
    202      */
    203     public int getState() {
    204         if (VDBG) log("getState()");
    205         final IBluetoothSap service = mService;
    206         if (service != null) {
    207             try {
    208                 return service.getState();
    209             } catch (RemoteException e) {
    210                 Log.e(TAG, e.toString());
    211             }
    212         } else {
    213             Log.w(TAG, "Proxy not attached to service");
    214             if (DBG) log(Log.getStackTraceString(new Throwable()));
    215         }
    216         return BluetoothSap.STATE_ERROR;
    217     }
    218 
    219     /**
    220      * Get the currently connected remote Bluetooth device (PCE).
    221      *
    222      * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
    223      * this proxy object is not connected to the Sap service.
    224      * @hide
    225      */
    226     public BluetoothDevice getClient() {
    227         if (VDBG) log("getClient()");
    228         final IBluetoothSap service = mService;
    229         if (service != null) {
    230             try {
    231                 return service.getClient();
    232             } catch (RemoteException e) {
    233                 Log.e(TAG, e.toString());
    234             }
    235         } else {
    236             Log.w(TAG, "Proxy not attached to service");
    237             if (DBG) log(Log.getStackTraceString(new Throwable()));
    238         }
    239         return null;
    240     }
    241 
    242     /**
    243      * Returns true if the specified Bluetooth device is connected.
    244      * Returns false if not connected, or if this proxy object is not
    245      * currently connected to the Sap service.
    246      *
    247      * @hide
    248      */
    249     public boolean isConnected(BluetoothDevice device) {
    250         if (VDBG) log("isConnected(" + device + ")");
    251         final IBluetoothSap service = mService;
    252         if (service != null) {
    253             try {
    254                 return service.isConnected(device);
    255             } catch (RemoteException e) {
    256                 Log.e(TAG, e.toString());
    257             }
    258         } else {
    259             Log.w(TAG, "Proxy not attached to service");
    260             if (DBG) log(Log.getStackTraceString(new Throwable()));
    261         }
    262         return false;
    263     }
    264 
    265     /**
    266      * Initiate connection. Initiation of outgoing connections is not
    267      * supported for SAP server.
    268      *
    269      * @hide
    270      */
    271     public boolean connect(BluetoothDevice device) {
    272         if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
    273         return false;
    274     }
    275 
    276     /**
    277      * Initiate disconnect.
    278      *
    279      * @param device Remote Bluetooth Device
    280      * @return false on error, true otherwise
    281      * @hide
    282      */
    283     public boolean disconnect(BluetoothDevice device) {
    284         if (DBG) log("disconnect(" + device + ")");
    285         final IBluetoothSap service = mService;
    286         if (service != null && isEnabled() && isValidDevice(device)) {
    287             try {
    288                 return service.disconnect(device);
    289             } catch (RemoteException e) {
    290                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    291                 return false;
    292             }
    293         }
    294         if (service == null) Log.w(TAG, "Proxy not attached to service");
    295         return false;
    296     }
    297 
    298     /**
    299      * Get the list of connected devices. Currently at most one.
    300      *
    301      * @return list of connected devices
    302      * @hide
    303      */
    304     public List<BluetoothDevice> getConnectedDevices() {
    305         if (DBG) log("getConnectedDevices()");
    306         final IBluetoothSap service = mService;
    307         if (service != null && isEnabled()) {
    308             try {
    309                 return service.getConnectedDevices();
    310             } catch (RemoteException e) {
    311                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    312                 return new ArrayList<BluetoothDevice>();
    313             }
    314         }
    315         if (service == null) Log.w(TAG, "Proxy not attached to service");
    316         return new ArrayList<BluetoothDevice>();
    317     }
    318 
    319     /**
    320      * Get the list of devices matching specified states. Currently at most one.
    321      *
    322      * @return list of matching devices
    323      * @hide
    324      */
    325     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    326         if (DBG) log("getDevicesMatchingStates()");
    327         final IBluetoothSap service = mService;
    328         if (service != null && isEnabled()) {
    329             try {
    330                 return service.getDevicesMatchingConnectionStates(states);
    331             } catch (RemoteException e) {
    332                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    333                 return new ArrayList<BluetoothDevice>();
    334             }
    335         }
    336         if (service == null) Log.w(TAG, "Proxy not attached to service");
    337         return new ArrayList<BluetoothDevice>();
    338     }
    339 
    340     /**
    341      * Get connection state of device
    342      *
    343      * @return device connection state
    344      * @hide
    345      */
    346     public int getConnectionState(BluetoothDevice device) {
    347         if (DBG) log("getConnectionState(" + device + ")");
    348         final IBluetoothSap service = mService;
    349         if (service != null && isEnabled() && isValidDevice(device)) {
    350             try {
    351                 return service.getConnectionState(device);
    352             } catch (RemoteException e) {
    353                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    354                 return BluetoothProfile.STATE_DISCONNECTED;
    355             }
    356         }
    357         if (service == null) Log.w(TAG, "Proxy not attached to service");
    358         return BluetoothProfile.STATE_DISCONNECTED;
    359     }
    360 
    361     /**
    362      * Set priority of the profile
    363      *
    364      * <p> The device should already be paired.
    365      *
    366      * @param device Paired bluetooth device
    367      * @param priority
    368      * @return true if priority is set, false on error
    369      * @hide
    370      */
    371     public boolean setPriority(BluetoothDevice device, int priority) {
    372         if (DBG) log("setPriority(" + device + ", " + priority + ")");
    373         final IBluetoothSap service = mService;
    374         if (service != null && isEnabled() && isValidDevice(device)) {
    375             if (priority != BluetoothProfile.PRIORITY_OFF
    376                     && priority != BluetoothProfile.PRIORITY_ON) {
    377                 return false;
    378             }
    379             try {
    380                 return service.setPriority(device, priority);
    381             } catch (RemoteException e) {
    382                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    383                 return false;
    384             }
    385         }
    386         if (service == null) Log.w(TAG, "Proxy not attached to service");
    387         return false;
    388     }
    389 
    390     /**
    391      * Get the priority of the profile.
    392      *
    393      * @param device Bluetooth device
    394      * @return priority of the device
    395      * @hide
    396      */
    397     public int getPriority(BluetoothDevice device) {
    398         if (VDBG) log("getPriority(" + device + ")");
    399         final IBluetoothSap service = mService;
    400         if (service != null && isEnabled() && isValidDevice(device)) {
    401             try {
    402                 return service.getPriority(device);
    403             } catch (RemoteException e) {
    404                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    405                 return PRIORITY_OFF;
    406             }
    407         }
    408         if (service == null) Log.w(TAG, "Proxy not attached to service");
    409         return PRIORITY_OFF;
    410     }
    411 
    412     private final ServiceConnection mConnection = new ServiceConnection() {
    413         public void onServiceConnected(ComponentName className, IBinder service) {
    414             if (DBG) log("Proxy object connected");
    415             mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
    416             if (mServiceListener != null) {
    417                 mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this);
    418             }
    419         }
    420 
    421         public void onServiceDisconnected(ComponentName className) {
    422             if (DBG) log("Proxy object disconnected");
    423             mService = null;
    424             if (mServiceListener != null) {
    425                 mServiceListener.onServiceDisconnected(BluetoothProfile.SAP);
    426             }
    427         }
    428     };
    429 
    430     private static void log(String msg) {
    431         Log.d(TAG, msg);
    432     }
    433 
    434     private boolean isEnabled() {
    435         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    436 
    437         if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) {
    438             return true;
    439         }
    440         log("Bluetooth is Not enabled");
    441         return false;
    442     }
    443 
    444     private static boolean isValidDevice(BluetoothDevice device) {
    445         return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
    446     }
    447 
    448 }
    449