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