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