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.RemoteException;
     24 import android.os.IBinder;
     25 import android.util.Log;
     26 
     27 /**
     28  * The Android Bluetooth API is not finalized, and *will* change. Use at your
     29  * own risk.
     30  *
     31  * Public API for controlling the Bluetooth Pbap Service. This includes
     32  * Bluetooth Phone book Access profile.
     33  * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
     34  * Service via IPC.
     35  *
     36  * Creating a BluetoothPbap object will create a binding with the
     37  * BluetoothPbap service. Users of this object should call close() when they
     38  * are finished with the BluetoothPbap, so that this proxy object can unbind
     39  * from the service.
     40  *
     41  * This BluetoothPbap object is not immediately bound to the
     42  * BluetoothPbap service. Use the ServiceListener interface to obtain a
     43  * notification when it is bound, this is especially important if you wish to
     44  * immediately call methods on BluetoothPbap after construction.
     45  *
     46  * Android only supports one connected Bluetooth Pce at a time.
     47  *
     48  * @hide
     49  */
     50 public class BluetoothPbap {
     51 
     52     private static final String TAG = "BluetoothPbap";
     53     private static final boolean DBG = true;
     54     private static final boolean VDBG = false;
     55 
     56     /** int extra for PBAP_STATE_CHANGED_ACTION */
     57     public static final String PBAP_STATE =
     58         "android.bluetooth.pbap.intent.PBAP_STATE";
     59     /** int extra for PBAP_STATE_CHANGED_ACTION */
     60     public static final String PBAP_PREVIOUS_STATE =
     61         "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE";
     62 
     63     /** Indicates the state of a pbap connection state has changed.
     64      *  This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and
     65      *  BluetoothIntent.ADDRESS extras.
     66      */
     67     public static final String PBAP_STATE_CHANGED_ACTION =
     68         "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED";
     69 
     70     private IBluetoothPbap mService;
     71     private final Context mContext;
     72     private ServiceListener mServiceListener;
     73     private BluetoothAdapter mAdapter;
     74 
     75     /** There was an error trying to obtain the state */
     76     public static final int STATE_ERROR        = -1;
     77     /** No client currently connected */
     78     public static final int STATE_DISCONNECTED = 0;
     79     /** Connection attempt in progress */
     80     public static final int STATE_CONNECTING   = 1;
     81     /** Client is currently connected */
     82     public static final int STATE_CONNECTED    = 2;
     83 
     84     public static final int RESULT_FAILURE = 0;
     85     public static final int RESULT_SUCCESS = 1;
     86     /** Connection canceled before completion. */
     87     public static final int RESULT_CANCELED = 2;
     88 
     89     /**
     90      * An interface for notifying Bluetooth PCE IPC clients when they have
     91      * been connected to the BluetoothPbap service.
     92      */
     93     public interface ServiceListener {
     94         /**
     95          * Called to notify the client when this proxy object has been
     96          * connected to the BluetoothPbap service. Clients must wait for
     97          * this callback before making IPC calls on the BluetoothPbap
     98          * service.
     99          */
    100         public void onServiceConnected(BluetoothPbap proxy);
    101 
    102         /**
    103          * Called to notify the client that this proxy object has been
    104          * disconnected from the BluetoothPbap service. Clients must not
    105          * make IPC calls on the BluetoothPbap service after this callback.
    106          * This callback will currently only occur if the application hosting
    107          * the BluetoothPbap service, but may be called more often in future.
    108          */
    109         public void onServiceDisconnected();
    110     }
    111 
    112     final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
    113             new IBluetoothStateChangeCallback.Stub() {
    114                 public void onBluetoothStateChange(boolean up) {
    115                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
    116                     if (!up) {
    117                         if (VDBG) Log.d(TAG,"Unbinding service...");
    118                         synchronized (mConnection) {
    119                             try {
    120                                 mService = null;
    121                                 mContext.unbindService(mConnection);
    122                             } catch (Exception re) {
    123                                 Log.e(TAG,"",re);
    124                             }
    125                         }
    126                     } else {
    127                         synchronized (mConnection) {
    128                             try {
    129                                 if (mService == null) {
    130                                     if (VDBG) Log.d(TAG,"Binding service...");
    131                                     doBind();
    132                                 }
    133                             } catch (Exception re) {
    134                                 Log.e(TAG,"",re);
    135                             }
    136                         }
    137                     }
    138                 }
    139         };
    140 
    141     /**
    142      * Create a BluetoothPbap proxy object.
    143      */
    144     public BluetoothPbap(Context context, ServiceListener l) {
    145         mContext = context;
    146         mServiceListener = l;
    147         mAdapter = BluetoothAdapter.getDefaultAdapter();
    148         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    149         if (mgr != null) {
    150             try {
    151                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
    152             } catch (RemoteException e) {
    153                 Log.e(TAG,"",e);
    154             }
    155         }
    156         doBind();
    157     }
    158 
    159     boolean doBind() {
    160         Intent intent = new Intent(IBluetoothPbap.class.getName());
    161         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
    162         intent.setComponent(comp);
    163         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
    164                 android.os.Process.myUserHandle())) {
    165             Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
    166             return false;
    167         }
    168         return true;
    169     }
    170 
    171     protected void finalize() throws Throwable {
    172         try {
    173             close();
    174         } finally {
    175             super.finalize();
    176         }
    177     }
    178 
    179     /**
    180      * Close the connection to the backing service.
    181      * Other public functions of BluetoothPbap will return default error
    182      * results once close() has been called. Multiple invocations of close()
    183      * are ok.
    184      */
    185     public synchronized void close() {
    186         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    187         if (mgr != null) {
    188             try {
    189                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
    190             } catch (Exception e) {
    191                 Log.e(TAG,"",e);
    192             }
    193         }
    194 
    195         synchronized (mConnection) {
    196             if (mService != null) {
    197                 try {
    198                     mService = null;
    199                     mContext.unbindService(mConnection);
    200                 } catch (Exception re) {
    201                     Log.e(TAG,"",re);
    202                 }
    203             }
    204         }
    205         mServiceListener = null;
    206     }
    207 
    208     /**
    209      * Get the current state of the BluetoothPbap service.
    210      * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
    211      *         object is currently not connected to the Pbap service.
    212      */
    213     public int getState() {
    214         if (VDBG) log("getState()");
    215         if (mService != null) {
    216             try {
    217                 return mService.getState();
    218             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    219         } else {
    220             Log.w(TAG, "Proxy not attached to service");
    221             if (DBG) log(Log.getStackTraceString(new Throwable()));
    222         }
    223         return BluetoothPbap.STATE_ERROR;
    224     }
    225 
    226     /**
    227      * Get the currently connected remote Bluetooth device (PCE).
    228      * @return The remote Bluetooth device, or null if not in connected or
    229      *         connecting state, or if this proxy object is not connected to
    230      *         the Pbap service.
    231      */
    232     public BluetoothDevice getClient() {
    233         if (VDBG) log("getClient()");
    234         if (mService != null) {
    235             try {
    236                 return mService.getClient();
    237             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    238         } else {
    239             Log.w(TAG, "Proxy not attached to service");
    240             if (DBG) log(Log.getStackTraceString(new Throwable()));
    241         }
    242         return null;
    243     }
    244 
    245     /**
    246      * Returns true if the specified Bluetooth device is connected (does not
    247      * include connecting). Returns false if not connected, or if this proxy
    248      * object is not currently connected to the Pbap service.
    249      */
    250     public boolean isConnected(BluetoothDevice device) {
    251         if (VDBG) log("isConnected(" + device + ")");
    252         if (mService != null) {
    253             try {
    254                 return mService.isConnected(device);
    255             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    256         } else {
    257             Log.w(TAG, "Proxy not attached to service");
    258             if (DBG) log(Log.getStackTraceString(new Throwable()));
    259         }
    260         return false;
    261     }
    262 
    263     /**
    264      * Disconnects the current Pbap client (PCE). Currently this call blocks,
    265      * it may soon be made asynchronous. Returns false if this proxy object is
    266      * not currently connected to the Pbap service.
    267      */
    268     public boolean disconnect() {
    269         if (DBG) log("disconnect()");
    270         if (mService != null) {
    271             try {
    272                 mService.disconnect();
    273                 return true;
    274             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    275         } else {
    276             Log.w(TAG, "Proxy not attached to service");
    277             if (DBG) log(Log.getStackTraceString(new Throwable()));
    278         }
    279         return false;
    280     }
    281 
    282     /**
    283      * Check class bits for possible PBAP support.
    284      * This is a simple heuristic that tries to guess if a device with the
    285      * given class bits might support PBAP. It is not accurate for all
    286      * devices. It tries to err on the side of false positives.
    287      * @return True if this device might support PBAP.
    288      */
    289     public static boolean doesClassMatchSink(BluetoothClass btClass) {
    290         // TODO optimize the rule
    291         switch (btClass.getDeviceClass()) {
    292         case BluetoothClass.Device.COMPUTER_DESKTOP:
    293         case BluetoothClass.Device.COMPUTER_LAPTOP:
    294         case BluetoothClass.Device.COMPUTER_SERVER:
    295         case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
    296             return true;
    297         default:
    298             return false;
    299         }
    300     }
    301 
    302     private final ServiceConnection mConnection = new ServiceConnection() {
    303         public void onServiceConnected(ComponentName className, IBinder service) {
    304             if (DBG) log("Proxy object connected");
    305             mService = IBluetoothPbap.Stub.asInterface(service);
    306             if (mServiceListener != null) {
    307                 mServiceListener.onServiceConnected(BluetoothPbap.this);
    308             }
    309         }
    310         public void onServiceDisconnected(ComponentName className) {
    311             if (DBG) log("Proxy object disconnected");
    312             mService = null;
    313             if (mServiceListener != null) {
    314                 mServiceListener.onServiceDisconnected();
    315             }
    316         }
    317     };
    318 
    319     private static void log(String msg) {
    320         Log.d(TAG, msg);
    321     }
    322 }
    323