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 */ BluetoothMap(Context context, ServiceListener l)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 doBind()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 finalize()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 */ close()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 */ getState()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 */ getClient()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 */ isConnected(BluetoothDevice device)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 */ connect(BluetoothDevice device)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 */ disconnect(BluetoothDevice device)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 */ doesClassMatchSink(BluetoothClass btClass)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 */ getConnectedDevices()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 */ getDevicesMatchingConnectionStates(int[] states)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 */ getConnectionState(BluetoothDevice device)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 */ setPriority(BluetoothDevice device, int priority)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 */ getPriority(BluetoothDevice device)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 log(String msg)388 private static void log(String msg) { 389 Log.d(TAG, msg); 390 } 391 isEnabled()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 } isValidDevice(BluetoothDevice device)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