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.os.ServiceManager;
     26 import android.util.Log;
     27 
     28 /**
     29  * The Android Bluetooth API is not finalized, and *will* change. Use at your
     30  * own risk.
     31  *
     32  * Public API for controlling the Bluetooth Pbap Service. This includes
     33  * Bluetooth Phone book Access profile.
     34  * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
     35  * Service via IPC.
     36  *
     37  * Creating a BluetoothPbap object will create a binding with the
     38  * BluetoothPbap service. Users of this object should call close() when they
     39  * are finished with the BluetoothPbap, so that this proxy object can unbind
     40  * from the service.
     41  *
     42  * This BluetoothPbap object is not immediately bound to the
     43  * BluetoothPbap service. Use the ServiceListener interface to obtain a
     44  * notification when it is bound, this is especially important if you wish to
     45  * immediately call methods on BluetoothPbap after construction.
     46  *
     47  * Android only supports one connected Bluetooth Pce at a time.
     48  *
     49  * @hide
     50  */
     51 public class BluetoothPbap {
     52 
     53     private static final String TAG = "BluetoothPbap";
     54     private static final boolean DBG = true;
     55     private static final boolean VDBG = false;
     56 
     57     /** int extra for PBAP_STATE_CHANGED_ACTION */
     58     public static final String PBAP_STATE =
     59         "android.bluetooth.pbap.intent.PBAP_STATE";
     60     /** int extra for PBAP_STATE_CHANGED_ACTION */
     61     public static final String PBAP_PREVIOUS_STATE =
     62         "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE";
     63 
     64     /** Indicates the state of a pbap connection state has changed.
     65      *  This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and
     66      *  BluetoothIntent.ADDRESS extras.
     67      */
     68     public static final String PBAP_STATE_CHANGED_ACTION =
     69         "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED";
     70 
     71     private IBluetoothPbap mService;
     72     private final Context mContext;
     73     private ServiceListener mServiceListener;
     74     private BluetoothAdapter mAdapter;
     75 
     76     /** There was an error trying to obtain the state */
     77     public static final int STATE_ERROR        = -1;
     78     /** No client currently connected */
     79     public static final int STATE_DISCONNECTED = 0;
     80     /** Connection attempt in progress */
     81     public static final int STATE_CONNECTING   = 1;
     82     /** Client is currently connected */
     83     public static final int STATE_CONNECTED    = 2;
     84 
     85     public static final int RESULT_FAILURE = 0;
     86     public static final int RESULT_SUCCESS = 1;
     87     /** Connection canceled before completion. */
     88     public static final int RESULT_CANCELED = 2;
     89 
     90     /**
     91      * An interface for notifying Bluetooth PCE IPC clients when they have
     92      * been connected to the BluetoothPbap service.
     93      */
     94     public interface ServiceListener {
     95         /**
     96          * Called to notify the client when this proxy object has been
     97          * connected to the BluetoothPbap service. Clients must wait for
     98          * this callback before making IPC calls on the BluetoothPbap
     99          * service.
    100          */
    101         public void onServiceConnected(BluetoothPbap proxy);
    102 
    103         /**
    104          * Called to notify the client that this proxy object has been
    105          * disconnected from the BluetoothPbap service. Clients must not
    106          * make IPC calls on the BluetoothPbap service after this callback.
    107          * This callback will currently only occur if the application hosting
    108          * the BluetoothPbap service, but may be called more often in future.
    109          */
    110         public void onServiceDisconnected();
    111     }
    112 
    113     final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
    114             new IBluetoothStateChangeCallback.Stub() {
    115                 public void onBluetoothStateChange(boolean up) {
    116                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
    117                     if (!up) {
    118                         if (VDBG) Log.d(TAG,"Unbinding service...");
    119                         synchronized (mConnection) {
    120                             try {
    121                                 mService = null;
    122                                 mContext.unbindService(mConnection);
    123                             } catch (Exception re) {
    124                                 Log.e(TAG,"",re);
    125                             }
    126                         }
    127                     } else {
    128                         synchronized (mConnection) {
    129                             try {
    130                                 if (mService == null) {
    131                                     if (VDBG) Log.d(TAG,"Binding service...");
    132                                     doBind();
    133                                 }
    134                             } catch (Exception re) {
    135                                 Log.e(TAG,"",re);
    136                             }
    137                         }
    138                     }
    139                 }
    140         };
    141 
    142     /**
    143      * Create a BluetoothPbap proxy object.
    144      */
    145     public BluetoothPbap(Context context, ServiceListener l) {
    146         mContext = context;
    147         mServiceListener = l;
    148         mAdapter = BluetoothAdapter.getDefaultAdapter();
    149         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    150         if (mgr != null) {
    151             try {
    152                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
    153             } catch (RemoteException e) {
    154                 Log.e(TAG,"",e);
    155             }
    156         }
    157         doBind();
    158     }
    159 
    160     boolean doBind() {
    161         Intent intent = new Intent(IBluetoothPbap.class.getName());
    162         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
    163         intent.setComponent(comp);
    164         if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
    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