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 = 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 an 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 
     74     /** There was an error trying to obtain the state */
     75     public static final int STATE_ERROR        = -1;
     76     /** No client currently connected */
     77     public static final int STATE_DISCONNECTED = 0;
     78     /** Connection attempt in progress */
     79     public static final int STATE_CONNECTING   = 1;
     80     /** Client is currently connected */
     81     public static final int STATE_CONNECTED    = 2;
     82 
     83     public static final int RESULT_FAILURE = 0;
     84     public static final int RESULT_SUCCESS = 1;
     85     /** Connection canceled before completion. */
     86     public static final int RESULT_CANCELED = 2;
     87 
     88     /**
     89      * An interface for notifying Bluetooth PCE IPC clients when they have
     90      * been connected to the BluetoothPbap service.
     91      */
     92     public interface ServiceListener {
     93         /**
     94          * Called to notify the client when this proxy object has been
     95          * connected to the BluetoothPbap service. Clients must wait for
     96          * this callback before making IPC calls on the BluetoothPbap
     97          * service.
     98          */
     99         public void onServiceConnected();
    100 
    101         /**
    102          * Called to notify the client that this proxy object has been
    103          * disconnected from the BluetoothPbap service. Clients must not
    104          * make IPC calls on the BluetoothPbap service after this callback.
    105          * This callback will currently only occur if the application hosting
    106          * the BluetoothPbap service, but may be called more often in future.
    107          */
    108         public void onServiceDisconnected();
    109     }
    110 
    111     /**
    112      * Create a BluetoothPbap proxy object.
    113      */
    114     public BluetoothPbap(Context context, ServiceListener l) {
    115         mContext = context;
    116         mServiceListener = l;
    117         if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) {
    118             Log.e(TAG, "Could not bind to Bluetooth Pbap Service");
    119         }
    120     }
    121 
    122     protected void finalize() throws Throwable {
    123         try {
    124             close();
    125         } finally {
    126             super.finalize();
    127         }
    128     }
    129 
    130     /**
    131      * Close the connection to the backing service.
    132      * Other public functions of BluetoothPbap will return default error
    133      * results once close() has been called. Multiple invocations of close()
    134      * are ok.
    135      */
    136     public synchronized void close() {
    137         if (mConnection != null) {
    138             mContext.unbindService(mConnection);
    139             mConnection = null;
    140         }
    141         mServiceListener = null;
    142     }
    143 
    144     /**
    145      * Get the current state of the BluetoothPbap service.
    146      * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
    147      *         object is currently not connected to the Pbap service.
    148      */
    149     public int getState() {
    150         if (DBG) log("getState()");
    151         if (mService != null) {
    152             try {
    153                 return mService.getState();
    154             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    155         } else {
    156             Log.w(TAG, "Proxy not attached to service");
    157             if (DBG) log(Log.getStackTraceString(new Throwable()));
    158         }
    159         return BluetoothPbap.STATE_ERROR;
    160     }
    161 
    162     /**
    163      * Get the currently connected remote Bluetooth device (PCE).
    164      * @return The remote Bluetooth device, or null if not in connected or
    165      *         connecting state, or if this proxy object is not connected to
    166      *         the Pbap service.
    167      */
    168     public BluetoothDevice getClient() {
    169         if (DBG) log("getClient()");
    170         if (mService != null) {
    171             try {
    172                 return mService.getClient();
    173             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    174         } else {
    175             Log.w(TAG, "Proxy not attached to service");
    176             if (DBG) log(Log.getStackTraceString(new Throwable()));
    177         }
    178         return null;
    179     }
    180 
    181     /**
    182      * Returns true if the specified Bluetooth device is connected (does not
    183      * include connecting). Returns false if not connected, or if this proxy
    184      * object is not currently connected to the Pbap service.
    185      */
    186     public boolean isConnected(BluetoothDevice device) {
    187         if (DBG) log("isConnected(" + device + ")");
    188         if (mService != null) {
    189             try {
    190                 return mService.isConnected(device);
    191             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    192         } else {
    193             Log.w(TAG, "Proxy not attached to service");
    194             if (DBG) log(Log.getStackTraceString(new Throwable()));
    195         }
    196         return false;
    197     }
    198 
    199     /**
    200      * Disconnects the current Pbap client (PCE). Currently this call blocks,
    201      * it may soon be made asynchronous. Returns false if this proxy object is
    202      * not currently connected to the Pbap service.
    203      */
    204     public boolean disconnect() {
    205         if (DBG) log("disconnect()");
    206         if (mService != null) {
    207             try {
    208                 mService.disconnect();
    209                 return true;
    210             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    211         } else {
    212             Log.w(TAG, "Proxy not attached to service");
    213             if (DBG) log(Log.getStackTraceString(new Throwable()));
    214         }
    215         return false;
    216     }
    217 
    218     /**
    219      * Check class bits for possible PBAP support.
    220      * This is a simple heuristic that tries to guess if a device with the
    221      * given class bits might support PBAP. It is not accurate for all
    222      * devices. It tries to err on the side of false positives.
    223      * @return True if this device might support PBAP.
    224      */
    225     public static boolean doesClassMatchSink(BluetoothClass btClass) {
    226         // TODO optimize the rule
    227         switch (btClass.getDeviceClass()) {
    228         case BluetoothClass.Device.COMPUTER_DESKTOP:
    229         case BluetoothClass.Device.COMPUTER_LAPTOP:
    230         case BluetoothClass.Device.COMPUTER_SERVER:
    231         case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
    232             return true;
    233         default:
    234             return false;
    235         }
    236     }
    237 
    238     private ServiceConnection mConnection = new ServiceConnection() {
    239         public void onServiceConnected(ComponentName className, IBinder service) {
    240             if (DBG) log("Proxy object connected");
    241             mService = IBluetoothPbap.Stub.asInterface(service);
    242             if (mServiceListener != null) {
    243                 mServiceListener.onServiceConnected();
    244             }
    245         }
    246         public void onServiceDisconnected(ComponentName className) {
    247             if (DBG) log("Proxy object disconnected");
    248             mService = null;
    249             if (mServiceListener != null) {
    250                 mServiceListener.onServiceDisconnected();
    251             }
    252         }
    253     };
    254 
    255     private static void log(String msg) {
    256         Log.d(TAG, msg);
    257     }
    258 }
    259