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                                     if (!mContext.bindService(
    133                                                         new Intent(IBluetoothPbap.class.getName()),
    134                                                         mConnection, 0)) {
    135                                         Log.e(TAG, "Could not bind to Bluetooth PBAP Service");
    136                                     }
    137                                 }
    138                             } catch (Exception re) {
    139                                 Log.e(TAG,"",re);
    140                             }
    141                         }
    142                     }
    143                 }
    144         };
    145 
    146     /**
    147      * Create a BluetoothPbap proxy object.
    148      */
    149     public BluetoothPbap(Context context, ServiceListener l) {
    150         mContext = context;
    151         mServiceListener = l;
    152         mAdapter = BluetoothAdapter.getDefaultAdapter();
    153         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    154         if (mgr != null) {
    155             try {
    156                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
    157             } catch (RemoteException e) {
    158                 Log.e(TAG,"",e);
    159             }
    160         }
    161         if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) {
    162             Log.e(TAG, "Could not bind to Bluetooth Pbap Service");
    163         }
    164     }
    165 
    166     protected void finalize() throws Throwable {
    167         try {
    168             close();
    169         } finally {
    170             super.finalize();
    171         }
    172     }
    173 
    174     /**
    175      * Close the connection to the backing service.
    176      * Other public functions of BluetoothPbap will return default error
    177      * results once close() has been called. Multiple invocations of close()
    178      * are ok.
    179      */
    180     public synchronized void close() {
    181         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    182         if (mgr != null) {
    183             try {
    184                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
    185             } catch (Exception e) {
    186                 Log.e(TAG,"",e);
    187             }
    188         }
    189 
    190         synchronized (mConnection) {
    191             if (mService != null) {
    192                 try {
    193                     mService = null;
    194                     mContext.unbindService(mConnection);
    195                     mConnection = null;
    196                 } catch (Exception re) {
    197                     Log.e(TAG,"",re);
    198                 }
    199             }
    200         }
    201         mServiceListener = null;
    202     }
    203 
    204     /**
    205      * Get the current state of the BluetoothPbap service.
    206      * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
    207      *         object is currently not connected to the Pbap service.
    208      */
    209     public int getState() {
    210         if (VDBG) log("getState()");
    211         if (mService != null) {
    212             try {
    213                 return mService.getState();
    214             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    215         } else {
    216             Log.w(TAG, "Proxy not attached to service");
    217             if (DBG) log(Log.getStackTraceString(new Throwable()));
    218         }
    219         return BluetoothPbap.STATE_ERROR;
    220     }
    221 
    222     /**
    223      * Get the currently connected remote Bluetooth device (PCE).
    224      * @return The remote Bluetooth device, or null if not in connected or
    225      *         connecting state, or if this proxy object is not connected to
    226      *         the Pbap service.
    227      */
    228     public BluetoothDevice getClient() {
    229         if (VDBG) log("getClient()");
    230         if (mService != null) {
    231             try {
    232                 return mService.getClient();
    233             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    234         } else {
    235             Log.w(TAG, "Proxy not attached to service");
    236             if (DBG) log(Log.getStackTraceString(new Throwable()));
    237         }
    238         return null;
    239     }
    240 
    241     /**
    242      * Returns true if the specified Bluetooth device is connected (does not
    243      * include connecting). Returns false if not connected, or if this proxy
    244      * object is not currently connected to the Pbap service.
    245      */
    246     public boolean isConnected(BluetoothDevice device) {
    247         if (VDBG) log("isConnected(" + device + ")");
    248         if (mService != null) {
    249             try {
    250                 return mService.isConnected(device);
    251             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    252         } else {
    253             Log.w(TAG, "Proxy not attached to service");
    254             if (DBG) log(Log.getStackTraceString(new Throwable()));
    255         }
    256         return false;
    257     }
    258 
    259     /**
    260      * Disconnects the current Pbap client (PCE). Currently this call blocks,
    261      * it may soon be made asynchronous. Returns false if this proxy object is
    262      * not currently connected to the Pbap service.
    263      */
    264     public boolean disconnect() {
    265         if (DBG) log("disconnect()");
    266         if (mService != null) {
    267             try {
    268                 mService.disconnect();
    269                 return true;
    270             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    271         } else {
    272             Log.w(TAG, "Proxy not attached to service");
    273             if (DBG) log(Log.getStackTraceString(new Throwable()));
    274         }
    275         return false;
    276     }
    277 
    278     /**
    279      * Check class bits for possible PBAP support.
    280      * This is a simple heuristic that tries to guess if a device with the
    281      * given class bits might support PBAP. It is not accurate for all
    282      * devices. It tries to err on the side of false positives.
    283      * @return True if this device might support PBAP.
    284      */
    285     public static boolean doesClassMatchSink(BluetoothClass btClass) {
    286         // TODO optimize the rule
    287         switch (btClass.getDeviceClass()) {
    288         case BluetoothClass.Device.COMPUTER_DESKTOP:
    289         case BluetoothClass.Device.COMPUTER_LAPTOP:
    290         case BluetoothClass.Device.COMPUTER_SERVER:
    291         case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
    292             return true;
    293         default:
    294             return false;
    295         }
    296     }
    297 
    298     private ServiceConnection mConnection = new ServiceConnection() {
    299         public void onServiceConnected(ComponentName className, IBinder service) {
    300             if (DBG) log("Proxy object connected");
    301             mService = IBluetoothPbap.Stub.asInterface(service);
    302             if (mServiceListener != null) {
    303                 mServiceListener.onServiceConnected(BluetoothPbap.this);
    304             }
    305         }
    306         public void onServiceDisconnected(ComponentName className) {
    307             if (DBG) log("Proxy object disconnected");
    308             mService = null;
    309             if (mServiceListener != null) {
    310                 mServiceListener.onServiceDisconnected();
    311             }
    312         }
    313     };
    314 
    315     private static void log(String msg) {
    316         Log.d(TAG, msg);
    317     }
    318 }
    319