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 final 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     }
    142 
    143     /**
    144      * Get the current state of the BluetoothPbap service.
    145      * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
    146      *         object is currently not connected to the Pbap service.
    147      */
    148     public int getState() {
    149         if (DBG) log("getState()");
    150         if (mService != null) {
    151             try {
    152                 return mService.getState();
    153             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    154         } else {
    155             Log.w(TAG, "Proxy not attached to service");
    156             if (DBG) log(Log.getStackTraceString(new Throwable()));
    157         }
    158         return BluetoothPbap.STATE_ERROR;
    159     }
    160 
    161     /**
    162      * Get the currently connected remote Bluetooth device (PCE).
    163      * @return The remote Bluetooth device, or null if not in connected or
    164      *         connecting state, or if this proxy object is not connected to
    165      *         the Pbap service.
    166      */
    167     public BluetoothDevice getClient() {
    168         if (DBG) log("getClient()");
    169         if (mService != null) {
    170             try {
    171                 return mService.getClient();
    172             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    173         } else {
    174             Log.w(TAG, "Proxy not attached to service");
    175             if (DBG) log(Log.getStackTraceString(new Throwable()));
    176         }
    177         return null;
    178     }
    179 
    180     /**
    181      * Returns true if the specified Bluetooth device is connected (does not
    182      * include connecting). Returns false if not connected, or if this proxy
    183      * object is not currently connected to the Pbap service.
    184      */
    185     public boolean isConnected(BluetoothDevice device) {
    186         if (DBG) log("isConnected(" + device + ")");
    187         if (mService != null) {
    188             try {
    189                 return mService.isConnected(device);
    190             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    191         } else {
    192             Log.w(TAG, "Proxy not attached to service");
    193             if (DBG) log(Log.getStackTraceString(new Throwable()));
    194         }
    195         return false;
    196     }
    197 
    198     /**
    199      * Disconnects the current Pbap client (PCE). Currently this call blocks,
    200      * it may soon be made asynchornous. Returns false if this proxy object is
    201      * not currently connected to the Pbap service.
    202      */
    203     public boolean disconnect() {
    204         if (DBG) log("disconnect()");
    205         if (mService != null) {
    206             try {
    207                 mService.disconnect();
    208                 return true;
    209             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    210         } else {
    211             Log.w(TAG, "Proxy not attached to service");
    212             if (DBG) log(Log.getStackTraceString(new Throwable()));
    213         }
    214         return false;
    215     }
    216 
    217     /**
    218      * Check class bits for possible PBAP support.
    219      * This is a simple heuristic that tries to guess if a device with the
    220      * given class bits might support PBAP. It is not accurate for all
    221      * devices. It tries to err on the side of false positives.
    222      * @return True if this device might support PBAP.
    223      */
    224     public static boolean doesClassMatchSink(BluetoothClass btClass) {
    225         // TODO optimize the rule
    226         switch (btClass.getDeviceClass()) {
    227         case BluetoothClass.Device.COMPUTER_DESKTOP:
    228         case BluetoothClass.Device.COMPUTER_LAPTOP:
    229         case BluetoothClass.Device.COMPUTER_SERVER:
    230         case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
    231             return true;
    232         default:
    233             return false;
    234         }
    235     }
    236 
    237     private ServiceConnection mConnection = new ServiceConnection() {
    238         public void onServiceConnected(ComponentName className, IBinder service) {
    239             if (DBG) log("Proxy object connected");
    240             mService = IBluetoothPbap.Stub.asInterface(service);
    241             if (mServiceListener != null) {
    242                 mServiceListener.onServiceConnected();
    243             }
    244         }
    245         public void onServiceDisconnected(ComponentName className) {
    246             if (DBG) log("Proxy object disconnected");
    247             mService = null;
    248             if (mServiceListener != null) {
    249                 mServiceListener.onServiceDisconnected();
    250             }
    251         }
    252     };
    253 
    254     private static void log(String msg) {
    255         Log.d(TAG, msg);
    256     }
    257 }
    258