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