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 java.util.List;
     20 import java.util.ArrayList;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.ServiceConnection;
     25 import android.os.RemoteException;
     26 import android.os.IBinder;
     27 import android.os.ServiceManager;
     28 import android.util.Log;
     29 
     30 /**
     31  * This class provides the APIs to control the Bluetooth MAP
     32  * Profile.
     33  *@hide
     34  */
     35 public final class BluetoothMap implements BluetoothProfile {
     36 
     37     private static final String TAG = "BluetoothMap";
     38     private static final boolean DBG = true;
     39     private static final boolean VDBG = false;
     40 
     41     public static final String ACTION_CONNECTION_STATE_CHANGED =
     42         "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
     43 
     44     private IBluetoothMap mService;
     45     private final Context mContext;
     46     private ServiceListener mServiceListener;
     47     private BluetoothAdapter mAdapter;
     48 
     49     /** There was an error trying to obtain the state */
     50     public static final int STATE_ERROR        = -1;
     51 
     52     public static final int RESULT_FAILURE = 0;
     53     public static final int RESULT_SUCCESS = 1;
     54     /** Connection canceled before completion. */
     55     public static final int RESULT_CANCELED = 2;
     56 
     57     final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
     58             new IBluetoothStateChangeCallback.Stub() {
     59                 public void onBluetoothStateChange(boolean up) {
     60                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
     61                     if (!up) {
     62                         if (VDBG) Log.d(TAG,"Unbinding service...");
     63                         synchronized (mConnection) {
     64                             try {
     65                                 mService = null;
     66                                 mContext.unbindService(mConnection);
     67                             } catch (Exception re) {
     68                                 Log.e(TAG,"",re);
     69                             }
     70                         }
     71                     } else {
     72                         synchronized (mConnection) {
     73                             try {
     74                                 if (mService == null) {
     75                                     if (VDBG) Log.d(TAG,"Binding service...");
     76                                     doBind();
     77                                 }
     78                             } catch (Exception re) {
     79                                 Log.e(TAG,"",re);
     80                             }
     81                         }
     82                     }
     83                 }
     84         };
     85 
     86     /**
     87      * Create a BluetoothMap proxy object.
     88      */
     89     /*package*/ BluetoothMap(Context context, ServiceListener l) {
     90         if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
     91         mContext = context;
     92         mServiceListener = l;
     93         mAdapter = BluetoothAdapter.getDefaultAdapter();
     94         IBluetoothManager mgr = mAdapter.getBluetoothManager();
     95         if (mgr != null) {
     96             try {
     97                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
     98             } catch (RemoteException e) {
     99                 Log.e(TAG,"",e);
    100             }
    101         }
    102         doBind();
    103     }
    104 
    105     boolean doBind() {
    106         Intent intent = new Intent(IBluetoothMap.class.getName());
    107         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
    108         intent.setComponent(comp);
    109         if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
    110             Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
    111             return false;
    112         }
    113         return true;
    114     }
    115 
    116     protected void finalize() throws Throwable {
    117         try {
    118             close();
    119         } finally {
    120             super.finalize();
    121         }
    122     }
    123 
    124     /**
    125      * Close the connection to the backing service.
    126      * Other public functions of BluetoothMap will return default error
    127      * results once close() has been called. Multiple invocations of close()
    128      * are ok.
    129      */
    130     public synchronized void close() {
    131         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    132         if (mgr != null) {
    133             try {
    134                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
    135             } catch (Exception e) {
    136                 Log.e(TAG,"",e);
    137             }
    138         }
    139 
    140         synchronized (mConnection) {
    141             if (mService != null) {
    142                 try {
    143                     mService = null;
    144                     mContext.unbindService(mConnection);
    145                 } catch (Exception re) {
    146                     Log.e(TAG,"",re);
    147                 }
    148             }
    149         }
    150         mServiceListener = null;
    151     }
    152 
    153     /**
    154      * Get the current state of the BluetoothMap service.
    155      * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
    156      *         object is currently not connected to the Map service.
    157      */
    158     public int getState() {
    159         if (VDBG) log("getState()");
    160         if (mService != null) {
    161             try {
    162                 return mService.getState();
    163             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    164         } else {
    165             Log.w(TAG, "Proxy not attached to service");
    166             if (DBG) log(Log.getStackTraceString(new Throwable()));
    167         }
    168         return BluetoothMap.STATE_ERROR;
    169     }
    170 
    171     /**
    172      * Get the currently connected remote Bluetooth device (PCE).
    173      * @return The remote Bluetooth device, or null if not in connected or
    174      *         connecting state, or if this proxy object is not connected to
    175      *         the Map service.
    176      */
    177     public BluetoothDevice getClient() {
    178         if (VDBG) log("getClient()");
    179         if (mService != null) {
    180             try {
    181                 return mService.getClient();
    182             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    183         } else {
    184             Log.w(TAG, "Proxy not attached to service");
    185             if (DBG) log(Log.getStackTraceString(new Throwable()));
    186         }
    187         return null;
    188     }
    189 
    190     /**
    191      * Returns true if the specified Bluetooth device is connected.
    192      * Returns false if not connected, or if this proxy object is not
    193      * currently connected to the Map service.
    194      */
    195     public boolean isConnected(BluetoothDevice device) {
    196         if (VDBG) log("isConnected(" + device + ")");
    197         if (mService != null) {
    198             try {
    199                 return mService.isConnected(device);
    200             } catch (RemoteException e) {Log.e(TAG, e.toString());}
    201         } else {
    202             Log.w(TAG, "Proxy not attached to service");
    203             if (DBG) log(Log.getStackTraceString(new Throwable()));
    204         }
    205         return false;
    206     }
    207 
    208     /**
    209      * Initiate connection. Initiation of outgoing connections is not
    210      * supported for MAP server.
    211      */
    212     public boolean connect(BluetoothDevice device) {
    213         if (DBG) log("connect(" + device + ")" + "not supported for MAPS");
    214         return false;
    215     }
    216 
    217     /**
    218      * Initiate disconnect.
    219      *
    220      * @param device Remote Bluetooth Device
    221      * @return false on error,
    222      *               true otherwise
    223      */
    224     public boolean disconnect(BluetoothDevice device) {
    225         if (DBG) log("disconnect(" + device + ")");
    226         if (mService != null && isEnabled() &&
    227             isValidDevice(device)) {
    228             try {
    229                 return mService.disconnect(device);
    230             } catch (RemoteException e) {
    231               Log.e(TAG, Log.getStackTraceString(new Throwable()));
    232               return false;
    233             }
    234         }
    235         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    236         return false;
    237     }
    238 
    239     /**
    240      * Check class bits for possible Map support.
    241      * This is a simple heuristic that tries to guess if a device with the
    242      * given class bits might support Map. It is not accurate for all
    243      * devices. It tries to err on the side of false positives.
    244      * @return True if this device might support Map.
    245      */
    246     public static boolean doesClassMatchSink(BluetoothClass btClass) {
    247         // TODO optimize the rule
    248         switch (btClass.getDeviceClass()) {
    249         case BluetoothClass.Device.COMPUTER_DESKTOP:
    250         case BluetoothClass.Device.COMPUTER_LAPTOP:
    251         case BluetoothClass.Device.COMPUTER_SERVER:
    252         case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
    253             return true;
    254         default:
    255             return false;
    256         }
    257     }
    258 
    259     /**
    260      * Get the list of connected devices. Currently at most one.
    261      *
    262      * @return list of connected devices
    263      */
    264     public List<BluetoothDevice> getConnectedDevices() {
    265         if (DBG) log("getConnectedDevices()");
    266         if (mService != null && isEnabled()) {
    267             try {
    268                 return mService.getConnectedDevices();
    269             } catch (RemoteException e) {
    270                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    271                 return new ArrayList<BluetoothDevice>();
    272             }
    273         }
    274         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    275         return new ArrayList<BluetoothDevice>();
    276     }
    277 
    278     /**
    279      * Get the list of devices matching specified states. Currently at most one.
    280      *
    281      * @return list of matching devices
    282      */
    283     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    284         if (DBG) log("getDevicesMatchingStates()");
    285         if (mService != null && isEnabled()) {
    286             try {
    287                 return mService.getDevicesMatchingConnectionStates(states);
    288             } catch (RemoteException e) {
    289                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    290                 return new ArrayList<BluetoothDevice>();
    291             }
    292         }
    293         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    294         return new ArrayList<BluetoothDevice>();
    295     }
    296 
    297     /**
    298      * Get connection state of device
    299      *
    300      * @return device connection state
    301      */
    302     public int getConnectionState(BluetoothDevice device) {
    303         if (DBG) log("getConnectionState(" + device + ")");
    304         if (mService != null && isEnabled() &&
    305             isValidDevice(device)) {
    306             try {
    307                 return mService.getConnectionState(device);
    308             } catch (RemoteException e) {
    309                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    310                 return BluetoothProfile.STATE_DISCONNECTED;
    311             }
    312         }
    313         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    314         return BluetoothProfile.STATE_DISCONNECTED;
    315     }
    316 
    317     /**
    318      * Set priority of the profile
    319      *
    320      * <p> The device should already be paired.
    321      *  Priority can be one of {@link #PRIORITY_ON} or
    322      * {@link #PRIORITY_OFF},
    323      *
    324      * @param device Paired bluetooth device
    325      * @param priority
    326      * @return true if priority is set, false on error
    327      */
    328     public boolean setPriority(BluetoothDevice device, int priority) {
    329         if (DBG) log("setPriority(" + device + ", " + priority + ")");
    330         if (mService != null && isEnabled() &&
    331             isValidDevice(device)) {
    332             if (priority != BluetoothProfile.PRIORITY_OFF &&
    333                 priority != BluetoothProfile.PRIORITY_ON) {
    334               return false;
    335             }
    336             try {
    337                 return mService.setPriority(device, priority);
    338             } catch (RemoteException e) {
    339                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    340                 return false;
    341             }
    342         }
    343         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    344         return false;
    345     }
    346 
    347     /**
    348      * Get the priority of the profile.
    349      *
    350      * <p> The priority can be any of:
    351      * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
    352      * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
    353      *
    354      * @param device Bluetooth device
    355      * @return priority of the device
    356      */
    357     public int getPriority(BluetoothDevice device) {
    358         if (VDBG) log("getPriority(" + device + ")");
    359         if (mService != null && isEnabled() &&
    360             isValidDevice(device)) {
    361             try {
    362                 return mService.getPriority(device);
    363             } catch (RemoteException e) {
    364                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    365                 return PRIORITY_OFF;
    366             }
    367         }
    368         if (mService == null) Log.w(TAG, "Proxy not attached to service");
    369         return PRIORITY_OFF;
    370     }
    371 
    372     private final ServiceConnection mConnection = new ServiceConnection() {
    373         public void onServiceConnected(ComponentName className, IBinder service) {
    374             if (DBG) log("Proxy object connected");
    375             mService = IBluetoothMap.Stub.asInterface(service);
    376             if (mServiceListener != null) {
    377                 mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this);
    378             }
    379         }
    380         public void onServiceDisconnected(ComponentName className) {
    381             if (DBG) log("Proxy object disconnected");
    382             mService = null;
    383             if (mServiceListener != null) {
    384                 mServiceListener.onServiceDisconnected(BluetoothProfile.MAP);
    385             }
    386         }
    387     };
    388 
    389     private static void log(String msg) {
    390         Log.d(TAG, msg);
    391     }
    392 
    393    private boolean isEnabled() {
    394         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    395         if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
    396         log("Bluetooth is Not enabled");
    397         return false;
    398     }
    399     private boolean isValidDevice(BluetoothDevice device) {
    400        if (device == null) return false;
    401 
    402        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
    403        return false;
    404     }
    405 
    406 
    407 }
    408