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