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