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