BluetoothDevice.java revision efa1dd716da3372cc74a201d11de5e0ef1a9fe9a
1/* 2 * Copyright (C) 2009 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 17package android.bluetooth; 18 19import android.content.Context; 20import android.os.IBinder; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.os.RemoteException; 24import android.os.ServiceManager; 25import android.util.Log; 26 27import java.io.IOException; 28import java.io.UnsupportedEncodingException; 29 30/** 31 * Represents a remote Bluetooth device. 32 * 33 * <p>Use {@link BluetoothAdapter#getRemoteDevice} to create a {@link 34 * BluetoothDevice}. 35 * 36 * <p>This class is really just a thin wrapper for a Bluetooth hardware 37 * address. Objects of this class are immutable. Operations on this class 38 * are performed on the remote Bluetooth hardware address, using the 39 * {@link BluetoothAdapter} that was used to create this {@link 40 * BluetoothDevice}. 41 * 42 * TODO: unhide more of this class 43 */ 44public final class BluetoothDevice implements Parcelable { 45 private static final String TAG = "BluetoothDevice"; 46 47 /** We do not have a link key for the remote device, and are therefore not 48 * bonded 49 * @hide*/ 50 public static final int BOND_NOT_BONDED = 0; 51 /** We have a link key for the remote device, and are probably bonded. 52 * @hide */ 53 public static final int BOND_BONDED = 1; 54 /** We are currently attempting bonding 55 * @hide */ 56 public static final int BOND_BONDING = 2; 57 58 /** Ask device picker to show all kinds of BT devices. 59 * @hide */ 60 public static final int DEVICE_PICKER_FILTER_TYPE_ALL = 0; 61 /** Ask device picker to show BT devices that support AUDIO profiles. 62 * @hide */ 63 public static final int DEVICE_PICKER_FILTER_TYPE_AUDIO = 1; 64 /** Ask device picker to show BT devices that support Object Transfer. 65 * @hide */ 66 public static final int DEVICE_PICKER_FILTER_TYPE_TRANSFER = 2; 67 68 //TODO: Unify these result codes in BluetoothResult or BluetoothError 69 /** A bond attempt failed because pins did not match, or remote device did 70 * not respond to pin request in time 71 * @hide */ 72 public static final int UNBOND_REASON_AUTH_FAILED = 1; 73 /** A bond attempt failed because the other side explicilty rejected 74 * bonding 75 * @hide */ 76 public static final int UNBOND_REASON_AUTH_REJECTED = 2; 77 /** A bond attempt failed because we canceled the bonding process 78 * @hide */ 79 public static final int UNBOND_REASON_AUTH_CANCELED = 3; 80 /** A bond attempt failed because we could not contact the remote device 81 * @hide */ 82 public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; 83 /** A bond attempt failed because a discovery is in progress 84 * @hide */ 85 public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; 86 /** An existing bond was explicitly revoked 87 * @hide */ 88 public static final int UNBOND_REASON_REMOVED = 6; 89 90 //TODO: Remove duplicates between here and BluetoothAdapter 91 /** The user will be prompted to enter a pin 92 * @hide */ 93 public static final int PAIRING_VARIANT_PIN = 0; 94 /** The user will be prompted to enter a passkey 95 * @hide */ 96 public static final int PAIRING_VARIANT_PASSKEY = 1; 97 /** The user will be prompted to confirm the passkey displayed on the screen 98 * @hide */ 99 public static final int PAIRING_VARIANT_CONFIRMATION = 2; 100 101 private static final int ADDRESS_LENGTH = 17; 102 103 private static IBluetooth sService; /* Guarenteed constant after first object constructed */ 104 105 private final String mAddress; 106 107 /** 108 * Create a new BluetoothDevice 109 * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", 110 * and is validated in this constructor. 111 * @param address valid Bluetooth MAC address 112 * @throws RuntimeException Bluetooth is not available on this platform 113 * @throws IllegalArgumentException address is invalid 114 * @hide 115 */ 116 /*package*/ BluetoothDevice(String address) { 117 synchronized (BluetoothDevice.class) { 118 if (sService == null) { 119 IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE); 120 if (b == null) { 121 throw new RuntimeException("Bluetooth service not available"); 122 } 123 sService = IBluetooth.Stub.asInterface(b); 124 } 125 } 126 127 if (!checkBluetoothAddress(address)) { 128 throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); 129 } 130 131 mAddress = address; 132 } 133 134 @Override 135 public boolean equals(Object o) { 136 if (o instanceof BluetoothDevice) { 137 return mAddress.equals(((BluetoothDevice)o).getAddress()); 138 } 139 return false; 140 } 141 142 @Override 143 public int hashCode() { 144 return mAddress.hashCode(); 145 } 146 147 /** 148 * Returns a string representation of this BluetoothDevice. 149 * <p>Currently this is the Bluetooth hardware address, for example 150 * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress} 151 * if you explicitly require the Bluetooth hardware address in case the 152 * {@link #toString} representation changes in the future. 153 * @return string representation of this BluetoothDevice 154 */ 155 @Override 156 public String toString() { 157 return mAddress; 158 } 159 160 /** @hide */ 161 public int describeContents() { 162 return 0; 163 } 164 165 /** @hide */ 166 public static final Parcelable.Creator<BluetoothDevice> CREATOR = 167 new Parcelable.Creator<BluetoothDevice>() { 168 public BluetoothDevice createFromParcel(Parcel in) { 169 return new BluetoothDevice(in.readString()); 170 } 171 public BluetoothDevice[] newArray(int size) { 172 return new BluetoothDevice[size]; 173 } 174 }; 175 176 /** @hide */ 177 public void writeToParcel(Parcel out, int flags) { 178 out.writeString(mAddress); 179 } 180 181 /** 182 * Returns the hardware address of this BluetoothDevice. 183 * <p> For example, "00:11:22:AA:BB:CC". 184 * @return Bluetooth hardware address as string 185 */ 186 public String getAddress() { 187 return mAddress; 188 } 189 190 /** 191 * Get the friendly Bluetooth name of the remote device. 192 * 193 * <p>The local adapter will automatically retrieve remote names when 194 * performing a device scan, and will cache them. This method just returns 195 * the name for this device from the cache. 196 * 197 * @return the Bluetooth name, or null if there was a problem. 198 * @hide 199 */ 200 public String getName() { 201 try { 202 return sService.getRemoteName(mAddress); 203 } catch (RemoteException e) {Log.e(TAG, "", e);} 204 return null; 205 } 206 207 /** 208 * Create a bonding with a remote bluetooth device. 209 * 210 * This is an asynchronous call. The result of this bonding attempt can be 211 * observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents. 212 * 213 * @param address the remote device Bluetooth address. 214 * @return false If there was an immediate problem creating the bonding, 215 * true otherwise. 216 * @hide 217 */ 218 public boolean createBond() { 219 try { 220 return sService.createBond(mAddress); 221 } catch (RemoteException e) {Log.e(TAG, "", e);} 222 return false; 223 } 224 225 /** 226 * Cancel an in-progress bonding request started with createBond. 227 * @hide 228 */ 229 public boolean cancelBondProcess() { 230 try { 231 return sService.cancelBondProcess(mAddress); 232 } catch (RemoteException e) {Log.e(TAG, "", e);} 233 return false; 234 } 235 236 /** 237 * Removes the remote device and the pairing information associated 238 * with it. 239 * 240 * @return true if the device was disconnected, false otherwise and on 241 * error. 242 * @hide 243 */ 244 public boolean removeBond() { 245 try { 246 return sService.removeBond(mAddress); 247 } catch (RemoteException e) {Log.e(TAG, "", e);} 248 return false; 249 } 250 251 /** 252 * Get the bonding state of a remote device. 253 * 254 * Result is one of: 255 * BluetoothError.* 256 * BOND_* 257 * 258 * @param address Bluetooth hardware address of the remote device to check. 259 * @return Result code 260 * @hide 261 */ 262 public int getBondState() { 263 try { 264 return sService.getBondState(mAddress); 265 } catch (RemoteException e) {Log.e(TAG, "", e);} 266 return BluetoothError.ERROR_IPC; 267 } 268 269 /** 270 * Get trust state of a remote device. 271 * @hide 272 */ 273 public boolean getTrustState() { 274 try { 275 return sService.getTrustState(mAddress); 276 } catch (RemoteException e) { 277 Log.e(TAG, "", e); 278 } 279 return false; 280 } 281 282 /** 283 * Set trust state for a remote device. 284 * @param value the trust state value (true or false) 285 * @hide 286 */ 287 public boolean setTrust(boolean value) { 288 try { 289 return sService.setTrust(mAddress, value); 290 } catch (RemoteException e) { 291 Log.e(TAG, "", e); 292 } 293 return false; 294 } 295 296 /** @hide */ 297 public int getBluetoothClass() { 298 try { 299 return sService.getRemoteClass(mAddress); 300 } catch (RemoteException e) {Log.e(TAG, "", e);} 301 return BluetoothError.ERROR_IPC; 302 } 303 304 /** @hide */ 305 public String[] getUuids() { 306 try { 307 return sService.getRemoteUuids(mAddress); 308 } catch (RemoteException e) {Log.e(TAG, "", e);} 309 return null; 310 } 311 312 /** @hide */ 313 public int getServiceChannel(String uuid) { 314 try { 315 return sService.getRemoteServiceChannel(mAddress, uuid); 316 } catch (RemoteException e) {Log.e(TAG, "", e);} 317 return BluetoothError.ERROR_IPC; 318 } 319 320 /** @hide */ 321 public boolean setPin(byte[] pin) { 322 try { 323 return sService.setPin(mAddress, pin); 324 } catch (RemoteException e) {Log.e(TAG, "", e);} 325 return false; 326 } 327 328 /** @hide */ 329 public boolean setPasskey(int passkey) { 330 try { 331 return sService.setPasskey(mAddress, passkey); 332 } catch (RemoteException e) {Log.e(TAG, "", e);} 333 return false; 334 } 335 336 /** @hide */ 337 public boolean setPairingConfirmation(boolean confirm) { 338 try { 339 return sService.setPairingConfirmation(mAddress, confirm); 340 } catch (RemoteException e) {Log.e(TAG, "", e);} 341 return false; 342 } 343 344 /** @hide */ 345 public boolean cancelPairingUserInput() { 346 try { 347 return sService.cancelPairingUserInput(mAddress); 348 } catch (RemoteException e) {Log.e(TAG, "", e);} 349 return false; 350 } 351 352 /** 353 * Create an RFCOMM {@link BluetoothSocket} ready to start a secure 354 * outgoing connection to this remote device. 355 * <p>The remote device will be authenticated and communication on this 356 * socket will be encrypted. 357 * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing 358 * connection. 359 * <p>Valid RFCOMM channels are in range 1 to 30. 360 * @param channel RFCOMM channel to connect to 361 * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection 362 * @throws IOException on error, for example Bluetooth not available, or 363 * insufficient permissions 364 */ 365 public BluetoothSocket createRfcommSocket(int channel) throws IOException { 366 return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel); 367 } 368 369 /** 370 * Construct an insecure RFCOMM socket ready to start an outgoing 371 * connection. 372 * Call #connect on the returned #BluetoothSocket to begin the connection. 373 * The remote device will not be authenticated and communication on this 374 * socket will not be encrypted. 375 * @param port remote port 376 * @return An RFCOMM BluetoothSocket 377 * @throws IOException On error, for example Bluetooth not available, or 378 * insufficient permissions. 379 * @hide 380 */ 381 public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { 382 return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port); 383 } 384 385 /** 386 * Construct a SCO socket ready to start an outgoing connection. 387 * Call #connect on the returned #BluetoothSocket to begin the connection. 388 * @return a SCO BluetoothSocket 389 * @throws IOException on error, for example Bluetooth not available, or 390 * insufficient permissions. 391 * @hide 392 */ 393 public BluetoothSocket createScoSocket() throws IOException { 394 return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1); 395 } 396 397 /** 398 * Check that a pin is valid and convert to byte array. 399 * 400 * Bluetooth pin's are 1 to 16 bytes of UTF8 characters. 401 * @param pin pin as java String 402 * @return the pin code as a UTF8 byte array, or null if it is an invalid 403 * Bluetooth pin. 404 * @hide 405 */ 406 public static byte[] convertPinToBytes(String pin) { 407 if (pin == null) { 408 return null; 409 } 410 byte[] pinBytes; 411 try { 412 pinBytes = pin.getBytes("UTF8"); 413 } catch (UnsupportedEncodingException uee) { 414 Log.e(TAG, "UTF8 not supported?!?"); // this should not happen 415 return null; 416 } 417 if (pinBytes.length <= 0 || pinBytes.length > 16) { 418 return null; 419 } 420 return pinBytes; 421 } 422 423 /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" 424 * @hide */ 425 public static boolean checkBluetoothAddress(String address) { 426 if (address == null || address.length() != ADDRESS_LENGTH) { 427 return false; 428 } 429 for (int i = 0; i < ADDRESS_LENGTH; i++) { 430 char c = address.charAt(i); 431 switch (i % 3) { 432 case 0: 433 case 1: 434 if (Character.digit(c, 16) != -1) { 435 break; // hex character, OK 436 } 437 return false; 438 case 2: 439 if (c == ':') { 440 break; // OK 441 } 442 return false; 443 } 444 } 445 return true; 446 } 447} 448