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