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