BluetoothDevice.java revision 076357b8567458d4b6dfdcf839ef751634cd2bfb
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
17package android.bluetooth;
18
19import android.os.RemoteException;
20import android.util.Log;
21
22import java.io.UnsupportedEncodingException;
23
24/**
25 * The Android Bluetooth API is not finalized, and *will* change. Use at your
26 * own risk.
27 *
28 * Manages the local Bluetooth device. Scan for devices, create bondings,
29 * power up and down the adapter.
30 *
31 * @hide
32 */
33public class BluetoothDevice {
34    /** Inquiry scan and page scan are both off.
35     *  Device is neither discoverable nor connectable */
36    public static final int SCAN_MODE_NONE = 0;
37    /** Page scan is on, inquiry scan is off.
38     *  Device is connectable, but not discoverable */
39    public static final int SCAN_MODE_CONNECTABLE = 1;
40    /** Page scan and inquiry scan are on.
41     *  Device is connectable and discoverable */
42    public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3;
43
44    public static final int RESULT_FAILURE = -1;
45    public static final int RESULT_SUCCESS = 0;
46
47    /** We do not have a link key for the remote device, and are therefore not
48     * bonded */
49    public static final int BOND_NOT_BONDED = 0;
50    /** We have a link key for the remote device, and are probably bonded. */
51    public static final int BOND_BONDED = 1;
52    /** We are currently attempting bonding */
53    public static final int BOND_BONDING = 2;
54
55    //TODO: Unify these result codes in BluetoothResult or BluetoothError
56    /** A bond attempt failed because pins did not match, or remote device did
57     * not respond to pin request in time */
58    public static final int UNBOND_REASON_AUTH_FAILED = 1;
59    /** A bond attempt failed because the other side explicilty rejected
60     * bonding */
61    public static final int UNBOND_REASON_AUTH_REJECTED = 2;
62    /** A bond attempt failed because we canceled the bonding process */
63    public static final int UNBOND_REASON_AUTH_CANCELED = 3;
64    /** A bond attempt failed because we could not contact the remote device */
65    public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4;
66    /** An existing bond was explicitly revoked */
67    public static final int UNBOND_REASON_REMOVED = 5;
68
69    private static final String TAG = "BluetoothDevice";
70
71    private final IBluetoothDevice mService;
72    /**
73     * @hide - hide this because it takes a parameter of type
74     * IBluetoothDevice, which is a System private class.
75     * Also note that Context.getSystemService is a factory that
76     * returns a BlueToothDevice. That is the right way to get
77     * a BluetoothDevice.
78     */
79    public BluetoothDevice(IBluetoothDevice service) {
80        mService = service;
81    }
82
83    /**
84     * Get the current status of Bluetooth hardware.
85     *
86     * @return true if Bluetooth enabled, false otherwise.
87     */
88    public boolean isEnabled() {
89        try {
90            return mService.isEnabled();
91        } catch (RemoteException e) {Log.e(TAG, "", e);}
92        return false;
93    }
94
95    /**
96     * Enable the Bluetooth device.
97     * Turn on the underlying hardware.
98     * This is an asynchronous call, BluetoothIntent.ENABLED_ACTION will be
99     * sent if and when the device is successfully enabled.
100     * @return false if we cannot enable the Bluetooth device. True does not
101     * imply the device was enabled, it only implies that so far there were no
102     * problems.
103     */
104    public boolean enable() {
105        return enable(null);
106    }
107
108    /**
109     * Enable the Bluetooth device.
110     * Turns on the underlying hardware.
111     * This is an asynchronous call. onEnableResult() of your callback will be
112     * called when the call is complete, with either RESULT_SUCCESS or
113     * RESULT_FAILURE.
114     *
115     * Your callback will be called from a binder thread, not the main thread.
116     *
117     * In addition to the callback, BluetoothIntent.ENABLED_ACTION will be
118     * broadcast if the device is successfully enabled.
119     *
120     * @param callback Your callback, null is ok.
121     * @return true if your callback was successfully registered, or false if
122     * there was an error, implying your callback will never be called.
123     */
124    public boolean enable(IBluetoothDeviceCallback callback) {
125        try {
126            return mService.enable(callback);
127        } catch (RemoteException e) {Log.e(TAG, "", e);}
128        return false;
129    }
130
131    /**
132     * Disable the Bluetooth device.
133     * This turns off the underlying hardware.
134     *
135     * @return true if successful, false otherwise.
136     */
137    public boolean disable() {
138        try {
139            return mService.disable();
140        } catch (RemoteException e) {Log.e(TAG, "", e);}
141        return false;
142    }
143
144    public String getAddress() {
145        try {
146            return mService.getAddress();
147        } catch (RemoteException e) {Log.e(TAG, "", e);}
148        return null;
149    }
150
151    /**
152     * Get the friendly Bluetooth name of this device.
153     *
154     * This name is visible to remote Bluetooth devices. Currently it is only
155     * possible to retrieve the Bluetooth name when Bluetooth is enabled.
156     *
157     * @return the Bluetooth name, or null if there was a problem.
158     */
159    public String getName() {
160        try {
161            return mService.getName();
162        } catch (RemoteException e) {Log.e(TAG, "", e);}
163        return null;
164    }
165
166    /**
167     * Set the friendly Bluetooth name of this device.
168     *
169     * This name is visible to remote Bluetooth devices. The Bluetooth Service
170     * is responsible for persisting this name.
171     *
172     * @param name the name to set
173     * @return     true, if the name was successfully set. False otherwise.
174     */
175    public boolean setName(String name) {
176        try {
177            return mService.setName(name);
178        } catch (RemoteException e) {Log.e(TAG, "", e);}
179        return false;
180    }
181
182    public String getVersion() {
183        try {
184            return mService.getVersion();
185        } catch (RemoteException e) {Log.e(TAG, "", e);}
186        return null;
187    }
188    public String getRevision() {
189        try {
190            return mService.getRevision();
191        } catch (RemoteException e) {Log.e(TAG, "", e);}
192        return null;
193    }
194    public String getManufacturer() {
195        try {
196            return mService.getManufacturer();
197        } catch (RemoteException e) {Log.e(TAG, "", e);}
198        return null;
199    }
200    public String getCompany() {
201        try {
202            return mService.getCompany();
203        } catch (RemoteException e) {Log.e(TAG, "", e);}
204        return null;
205    }
206
207    /**
208     * Get the current scan mode.
209     * Used to determine if the local device is connectable and/or discoverable
210     * @return Scan mode, one of SCAN_MODE_* or an error code
211     */
212    public int getScanMode() {
213        try {
214            return mService.getScanMode();
215        } catch (RemoteException e) {Log.e(TAG, "", e);}
216        return BluetoothError.ERROR_IPC;
217    }
218
219    /**
220     * Set the current scan mode.
221     * Used to make the local device connectable and/or discoverable
222     * @param scanMode One of SCAN_MODE_*
223     */
224    public void setScanMode(int scanMode) {
225        try {
226            mService.setScanMode(scanMode);
227        } catch (RemoteException e) {Log.e(TAG, "", e);}
228    }
229
230    public int getDiscoverableTimeout() {
231        try {
232            return mService.getDiscoverableTimeout();
233        } catch (RemoteException e) {Log.e(TAG, "", e);}
234        return -1;
235    }
236    public void setDiscoverableTimeout(int timeout) {
237        try {
238            mService.setDiscoverableTimeout(timeout);
239        } catch (RemoteException e) {Log.e(TAG, "", e);}
240    }
241
242    public boolean startDiscovery() {
243        return startDiscovery(true);
244    }
245    public boolean startDiscovery(boolean resolveNames) {
246        try {
247            return mService.startDiscovery(resolveNames);
248        } catch (RemoteException e) {Log.e(TAG, "", e);}
249        return false;
250    }
251
252    public void cancelDiscovery() {
253        try {
254            mService.cancelDiscovery();
255        } catch (RemoteException e) {Log.e(TAG, "", e);}
256    }
257
258    public boolean isDiscovering() {
259        try {
260            return mService.isDiscovering();
261        } catch (RemoteException e) {Log.e(TAG, "", e);}
262        return false;
263    }
264
265    public boolean startPeriodicDiscovery() {
266        try {
267            return mService.startPeriodicDiscovery();
268        } catch (RemoteException e) {Log.e(TAG, "", e);}
269        return false;
270    }
271    public boolean stopPeriodicDiscovery() {
272        try {
273            return mService.stopPeriodicDiscovery();
274        } catch (RemoteException e) {Log.e(TAG, "", e);}
275        return false;
276    }
277    public boolean isPeriodicDiscovery() {
278        try {
279            return mService.isPeriodicDiscovery();
280        } catch (RemoteException e) {Log.e(TAG, "", e);}
281        return false;
282    }
283
284    public String[] listRemoteDevices() {
285        try {
286            return mService.listRemoteDevices();
287        } catch (RemoteException e) {Log.e(TAG, "", e);}
288        return null;
289    }
290
291    /**
292     * List remote devices that have a low level (ACL) connection.
293     *
294     * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
295     * an ACL connection even when not paired - this is common for SDP queries
296     * or for in-progress pairing requests.
297     *
298     * In most cases you probably want to test if a higher level protocol is
299     * connected, rather than testing ACL connections.
300     *
301     * @return bluetooth hardware addresses of remote devices with a current
302     *         ACL connection. Array size is 0 if no devices have a
303     *         connection. Null on error.
304     */
305    public String[] listAclConnections() {
306        try {
307            return mService.listAclConnections();
308        } catch (RemoteException e) {Log.e(TAG, "", e);}
309        return null;
310    }
311
312    /**
313     * Check if a specified remote device has a low level (ACL) connection.
314     *
315     * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
316     * an ACL connection even when not paired - this is common for SDP queries
317     * or for in-progress pairing requests.
318     *
319     * In most cases you probably want to test if a higher level protocol is
320     * connected, rather than testing ACL connections.
321     *
322     * @param address the Bluetooth hardware address you want to check.
323     * @return true if there is an ACL connection, false otherwise and on
324     *         error.
325     */
326    public boolean isAclConnected(String address) {
327        try {
328            return mService.isAclConnected(address);
329        } catch (RemoteException e) {Log.e(TAG, "", e);}
330        return false;
331    }
332
333    /**
334     * Perform a low level (ACL) disconnection of a remote device.
335     *
336     * This forcably disconnects the ACL layer connection to a remote device,
337     * which will cause all RFCOMM, SDP and L2CAP connections to this remote
338     * device to close.
339     *
340     * @param address the Bluetooth hardware address you want to disconnect.
341     * @return true if the device was disconnected, false otherwise and on
342     *         error.
343     */
344    public boolean disconnectRemoteDeviceAcl(String address) {
345        try {
346            return mService.disconnectRemoteDeviceAcl(address);
347        } catch (RemoteException e) {Log.e(TAG, "", e);}
348        return false;
349    }
350
351    /**
352     * Create a bonding with a remote bluetooth device.
353     *
354     * This is an asynchronous call. The result of this bonding attempt can be
355     * observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents.
356     *
357     * @param address the remote device Bluetooth address.
358     * @return false If there was an immediate problem creating the bonding,
359     *         true otherwise.
360     */
361    public boolean createBond(String address) {
362        try {
363            return mService.createBond(address);
364        } catch (RemoteException e) {Log.e(TAG, "", e);}
365        return false;
366    }
367
368    /**
369     * Cancel an in-progress bonding request started with createBond.
370     */
371    public boolean cancelBondProcess(String address) {
372        try {
373            return mService.cancelBondProcess(address);
374        } catch (RemoteException e) {Log.e(TAG, "", e);}
375        return false;
376    }
377
378    /**
379     * Remove an already exisiting bonding (delete the link key).
380     */
381    public boolean removeBond(String address) {
382        try {
383            return mService.removeBond(address);
384        } catch (RemoteException e) {Log.e(TAG, "", e);}
385        return false;
386    }
387
388    /**
389     * List remote devices that are bonded (paired) to the local device.
390     *
391     * Bonding (pairing) is the process by which the user enters a pin code for
392     * the device, which generates a shared link key, allowing for
393     * authentication and encryption of future connections. In Android we
394     * require bonding before RFCOMM or SCO connections can be made to a remote
395     * device.
396     *
397     * This function lists which remote devices we have a link key for. It does
398     * not cause any RF transmission, and does not check if the remote device
399     * still has it's link key with us. If the other side no longer has its
400     * link key then the RFCOMM or SCO connection attempt will result in an
401     * error.
402     *
403     * This function does not check if the remote device is in range.
404     *
405     * Remote devices that have an in-progress bonding attempt are not
406     * returned.
407     *
408     * @return bluetooth hardware addresses of remote devices that are
409     *         bonded. Array size is 0 if no devices are bonded. Null on error.
410     */
411    public String[] listBonds() {
412        try {
413            return mService.listBonds();
414        } catch (RemoteException e) {Log.e(TAG, "", e);}
415        return null;
416    }
417
418    /**
419     * Get the bonding state of a remote device.
420     *
421     * Result is one of:
422     * BluetoothError.*
423     * BOND_*
424     *
425     * @param address Bluetooth hardware address of the remote device to check.
426     * @return Result code
427     */
428    public int getBondState(String address) {
429        try {
430            return mService.getBondState(address);
431        } catch (RemoteException e) {Log.e(TAG, "", e);}
432        return BluetoothError.ERROR_IPC;
433    }
434
435    public String getRemoteName(String address) {
436        try {
437            return mService.getRemoteName(address);
438        } catch (RemoteException e) {Log.e(TAG, "", e);}
439        return null;
440    }
441
442    public String getRemoteVersion(String address) {
443        try {
444            return mService.getRemoteVersion(address);
445        } catch (RemoteException e) {Log.e(TAG, "", e);}
446        return null;
447    }
448    public String getRemoteRevision(String address) {
449        try {
450            return mService.getRemoteRevision(address);
451        } catch (RemoteException e) {Log.e(TAG, "", e);}
452        return null;
453    }
454    public String getRemoteManufacturer(String address) {
455        try {
456            return mService.getRemoteManufacturer(address);
457        } catch (RemoteException e) {Log.e(TAG, "", e);}
458        return null;
459    }
460    public String getRemoteCompany(String address) {
461        try {
462            return mService.getRemoteCompany(address);
463        } catch (RemoteException e) {Log.e(TAG, "", e);}
464        return null;
465    }
466
467    /**
468     * Returns the RFCOMM channel associated with the 16-byte UUID on
469     * the remote Bluetooth address.
470     *
471     * Performs a SDP ServiceSearchAttributeRequest transaction. The provided
472     * uuid is verified in the returned record. If there was a problem, or the
473     * specified uuid does not exist, -1 is returned.
474     */
475    public boolean getRemoteServiceChannel(String address, short uuid16,
476            IBluetoothDeviceCallback callback) {
477        try {
478            return mService.getRemoteServiceChannel(address, uuid16, callback);
479        } catch (RemoteException e) {Log.e(TAG, "", e);}
480        return false;
481    }
482
483    /**
484     * Get the major, minor and servics classes of a remote device.
485     * These classes are encoded as a 32-bit integer. See BluetoothClass.
486     * @param address remote device
487     * @return 32-bit class suitable for use with BluetoothClass.
488     */
489    public int getRemoteClass(String address) {
490        try {
491            return mService.getRemoteClass(address);
492        } catch (RemoteException e) {Log.e(TAG, "", e);}
493        return BluetoothClass.ERROR;
494    }
495
496    public byte[] getRemoteFeatures(String address) {
497        try {
498            return mService.getRemoteFeatures(address);
499        } catch (RemoteException e) {Log.e(TAG, "", e);}
500        return null;
501    }
502    public String lastSeen(String address) {
503        try {
504            return mService.lastSeen(address);
505        } catch (RemoteException e) {Log.e(TAG, "", e);}
506        return null;
507    }
508    public String lastUsed(String address) {
509        try {
510            return mService.lastUsed(address);
511        } catch (RemoteException e) {Log.e(TAG, "", e);}
512        return null;
513    }
514
515    public boolean setPin(String address, byte[] pin) {
516        try {
517            return mService.setPin(address, pin);
518        } catch (RemoteException e) {Log.e(TAG, "", e);}
519        return false;
520    }
521    public boolean cancelPin(String address) {
522        try {
523            return mService.cancelPin(address);
524        } catch (RemoteException e) {Log.e(TAG, "", e);}
525        return false;
526    }
527
528    /**
529     * Check that a pin is valid and convert to byte array.
530     *
531     * Bluetooth pin's are 1 to 16 bytes of UTF8 characters.
532     * @param pin pin as java String
533     * @return the pin code as a UTF8 byte array, or null if it is an invalid
534     *         Bluetooth pin.
535     */
536    public static byte[] convertPinToBytes(String pin) {
537        if (pin == null) {
538            return null;
539        }
540        byte[] pinBytes;
541        try {
542            pinBytes = pin.getBytes("UTF8");
543        } catch (UnsupportedEncodingException uee) {
544            Log.e(TAG, "UTF8 not supported?!?");  // this should not happen
545            return null;
546        }
547        if (pinBytes.length <= 0 || pinBytes.length > 16) {
548            return null;
549        }
550        return pinBytes;
551    }
552
553
554    private static final int ADDRESS_LENGTH = 17;
555    /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */
556    public static boolean checkBluetoothAddress(String address) {
557        if (address == null || address.length() != ADDRESS_LENGTH) {
558            return false;
559        }
560        for (int i = 0; i < ADDRESS_LENGTH; i++) {
561            char c = address.charAt(i);
562            switch (i % 3) {
563            case 0:
564            case 1:
565                if (Character.digit(c, 16) != -1) {
566                    break;  // hex character, OK
567                }
568                return false;
569            case 2:
570                if (c == ':') {
571                    break;  // OK
572                }
573                return false;
574            }
575        }
576        return true;
577    }
578}
579