BluetoothAdapter.java revision e16113998bb10f39ee1384bfc497aceeeb193440
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.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import android.os.Binder;
22import android.os.Handler;
23import android.os.IBinder;
24import android.os.Message;
25import android.os.ParcelUuid;
26import android.os.RemoteException;
27import android.os.ServiceManager;
28import android.util.Log;
29
30import java.io.IOException;
31import java.util.Collections;
32import java.util.HashSet;
33import java.util.LinkedList;
34import java.util.Random;
35import java.util.Set;
36import java.util.UUID;
37
38/**
39 * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
40 * lets you perform fundamental Bluetooth tasks, such as initiate
41 * device discovery, query a list of bonded (paired) devices,
42 * instantiate a {@link BluetoothDevice} using a known MAC address, and create
43 * a {@link BluetoothServerSocket} to listen for connection requests from other
44 * devices.
45 *
46 * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth
47 * adapter, call the static {@link #getDefaultAdapter} method.
48 * Fundamentally, this is your starting point for all
49 * Bluetooth actions. Once you have the local adapter, you can get a set of
50 * {@link BluetoothDevice} objects representing all paired devices with
51 * {@link #getBondedDevices()}; start device discovery with
52 * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
53 * listen for incoming connection requests with
54 * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}.
55 *
56 * <p class="note"><strong>Note:</strong>
57 * Most methods require the {@link android.Manifest.permission#BLUETOOTH}
58 * permission and some also require the
59 * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
60 *
61 * {@see BluetoothDevice}
62 * {@see BluetoothServerSocket}
63 */
64public final class BluetoothAdapter {
65    private static final String TAG = "BluetoothAdapter";
66    private static final boolean DBG = false;
67
68    /**
69     * Sentinel error value for this class. Guaranteed to not equal any other
70     * integer constant in this class. Provided as a convenience for functions
71     * that require a sentinel error value, for example:
72     * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
73     * BluetoothAdapter.ERROR)</code>
74     */
75    public static final int ERROR = Integer.MIN_VALUE;
76
77    /**
78     * Broadcast Action: The state of the local Bluetooth adapter has been
79     * changed.
80     * <p>For example, Bluetooth has been turned on or off.
81     * <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link
82     * #EXTRA_PREVIOUS_STATE} containing the new and old states
83     * respectively.
84     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
85     */
86    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
87    public static final String ACTION_STATE_CHANGED =
88            "android.bluetooth.adapter.action.STATE_CHANGED";
89
90    /**
91     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
92     * intents to request the current power state. Possible values are:
93     * {@link #STATE_OFF},
94     * {@link #STATE_TURNING_ON},
95     * {@link #STATE_ON},
96     * {@link #STATE_TURNING_OFF},
97     */
98    public static final String EXTRA_STATE =
99            "android.bluetooth.adapter.extra.STATE";
100    /**
101     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
102     * intents to request the previous power state. Possible values are:
103     * {@link #STATE_OFF},
104     * {@link #STATE_TURNING_ON},
105     * {@link #STATE_ON},
106     * {@link #STATE_TURNING_OFF},
107     */
108    public static final String EXTRA_PREVIOUS_STATE =
109            "android.bluetooth.adapter.extra.PREVIOUS_STATE";
110
111    /**
112     * Indicates the local Bluetooth adapter is off.
113     */
114    public static final int STATE_OFF = 10;
115    /**
116     * Indicates the local Bluetooth adapter is turning on. However local
117     * clients should wait for {@link #STATE_ON} before attempting to
118     * use the adapter.
119     */
120    public static final int STATE_TURNING_ON = 11;
121    /**
122     * Indicates the local Bluetooth adapter is on, and ready for use.
123     */
124    public static final int STATE_ON = 12;
125    /**
126     * Indicates the local Bluetooth adapter is turning off. Local clients
127     * should immediately attempt graceful disconnection of any remote links.
128     */
129    public static final int STATE_TURNING_OFF = 13;
130
131    /**
132     * Activity Action: Show a system activity that requests discoverable mode.
133     * This activity will also request the user to turn on Bluetooth if it
134     * is not currently enabled.
135     * <p>Discoverable mode is equivalent to {@link
136     * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see
137     * this Bluetooth adapter when they perform a discovery.
138     * <p>For privacy, Android is not discoverable by default.
139     * <p>The sender of this Intent can optionally use extra field {@link
140     * #EXTRA_DISCOVERABLE_DURATION} to request the duration of
141     * discoverability. Currently the default duration is 120 seconds, and
142     * maximum duration is capped at 300 seconds for each request.
143     * <p>Notification of the result of this activity is posted using the
144     * {@link android.app.Activity#onActivityResult} callback. The
145     * <code>resultCode</code>
146     * will be the duration (in seconds) of discoverability or
147     * {@link android.app.Activity#RESULT_CANCELED} if the user rejected
148     * discoverability or an error has occurred.
149     * <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED}
150     * for global notification whenever the scan mode changes. For example, an
151     * application can be notified when the device has ended discoverability.
152     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
153     */
154    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
155    public static final String ACTION_REQUEST_DISCOVERABLE =
156            "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
157
158    /**
159     * Used as an optional int extra field in {@link
160     * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration
161     * for discoverability in seconds. The current default is 120 seconds, and
162     * requests over 300 seconds will be capped. These values could change.
163     */
164    public static final String EXTRA_DISCOVERABLE_DURATION =
165            "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
166
167    /**
168     * Activity Action: Show a system activity that allows the user to turn on
169     * Bluetooth.
170     * <p>This system activity will return once Bluetooth has completed turning
171     * on, or the user has decided not to turn Bluetooth on.
172     * <p>Notification of the result of this activity is posted using the
173     * {@link android.app.Activity#onActivityResult} callback. The
174     * <code>resultCode</code>
175     * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been
176     * turned on or {@link android.app.Activity#RESULT_CANCELED} if the user
177     * has rejected the request or an error has occurred.
178     * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
179     * for global notification whenever Bluetooth is turned on or off.
180     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
181     */
182    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
183    public static final String ACTION_REQUEST_ENABLE =
184            "android.bluetooth.adapter.action.REQUEST_ENABLE";
185
186    /**
187     * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter
188     * has changed.
189     * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
190     * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes
191     * respectively.
192     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
193     */
194    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
195    public static final String ACTION_SCAN_MODE_CHANGED =
196            "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
197
198    /**
199     * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
200     * intents to request the current scan mode. Possible values are:
201     * {@link #SCAN_MODE_NONE},
202     * {@link #SCAN_MODE_CONNECTABLE},
203     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
204     */
205    public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE";
206    /**
207     * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
208     * intents to request the previous scan mode. Possible values are:
209     * {@link #SCAN_MODE_NONE},
210     * {@link #SCAN_MODE_CONNECTABLE},
211     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
212     */
213    public static final String EXTRA_PREVIOUS_SCAN_MODE =
214            "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE";
215
216    /**
217     * Indicates that both inquiry scan and page scan are disabled on the local
218     * Bluetooth adapter. Therefore this device is neither discoverable
219     * nor connectable from remote Bluetooth devices.
220     */
221    public static final int SCAN_MODE_NONE = 20;
222    /**
223     * Indicates that inquiry scan is disabled, but page scan is enabled on the
224     * local Bluetooth adapter. Therefore this device is not discoverable from
225     * remote Bluetooth devices, but is connectable from remote devices that
226     * have previously discovered this device.
227     */
228    public static final int SCAN_MODE_CONNECTABLE = 21;
229    /**
230     * Indicates that both inquiry scan and page scan are enabled on the local
231     * Bluetooth adapter. Therefore this device is both discoverable and
232     * connectable from remote Bluetooth devices.
233     */
234    public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;
235
236
237    /**
238     * Broadcast Action: The local Bluetooth adapter has started the remote
239     * device discovery process.
240     * <p>This usually involves an inquiry scan of about 12 seconds, followed
241     * by a page scan of each new device to retrieve its Bluetooth name.
242     * <p>Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as
243     * remote Bluetooth devices are found.
244     * <p>Device discovery is a heavyweight procedure. New connections to
245     * remote Bluetooth devices should not be attempted while discovery is in
246     * progress, and existing connections will experience limited bandwidth
247     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
248     * discovery.
249     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
250     */
251    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
252    public static final String ACTION_DISCOVERY_STARTED =
253            "android.bluetooth.adapter.action.DISCOVERY_STARTED";
254    /**
255     * Broadcast Action: The local Bluetooth adapter has finished the device
256     * discovery process.
257     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
258     */
259    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
260    public static final String ACTION_DISCOVERY_FINISHED =
261            "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
262
263    /**
264     * Broadcast Action: The local Bluetooth adapter has changed its friendly
265     * Bluetooth name.
266     * <p>This name is visible to remote Bluetooth devices.
267     * <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing
268     * the name.
269     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
270     */
271    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
272    public static final String ACTION_LOCAL_NAME_CHANGED =
273            "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
274    /**
275     * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED}
276     * intents to request the local Bluetooth name.
277     */
278    public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME";
279
280    /** @hide */
281    public static final String BLUETOOTH_SERVICE = "bluetooth";
282
283    private static final int ADDRESS_LENGTH = 17;
284
285    /**
286     * Lazyily initialized singleton. Guaranteed final after first object
287     * constructed.
288     */
289    private static BluetoothAdapter sAdapter;
290
291    private final IBluetooth mService;
292
293    /**
294     * Get a handle to the default local Bluetooth adapter.
295     * <p>Currently Android only supports one Bluetooth adapter, but the API
296     * could be extended to support more. This will always return the default
297     * adapter.
298     * @return the default local adapter, or null if Bluetooth is not supported
299     *         on this hardware platform
300     */
301    public static synchronized BluetoothAdapter getDefaultAdapter() {
302        if (sAdapter == null) {
303            IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
304            if (b != null) {
305                IBluetooth service = IBluetooth.Stub.asInterface(b);
306                sAdapter = new BluetoothAdapter(service);
307            }
308        }
309        return sAdapter;
310    }
311
312    /**
313     * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
314     * @hide
315     */
316    public BluetoothAdapter(IBluetooth service) {
317        if (service == null) {
318            throw new IllegalArgumentException("service is null");
319        }
320        mService = service;
321    }
322
323    /**
324     * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
325     * address.
326     * <p>Valid Bluetooth hardware addresses must be upper case, in a format
327     * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is
328     * available to validate a Bluetooth address.
329     * <p>A {@link BluetoothDevice} will always be returned for a valid
330     * hardware address, even if this adapter has never seen that device.
331     *
332     * @param address valid Bluetooth MAC address
333     * @throws IllegalArgumentException if address is invalid
334     */
335    public BluetoothDevice getRemoteDevice(String address) {
336        return new BluetoothDevice(address);
337    }
338
339    /**
340     * Return true if Bluetooth is currently enabled and ready for use.
341     * <p>Equivalent to:
342     * <code>getBluetoothState() == STATE_ON</code>
343     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
344     *
345     * @return true if the local adapter is turned on
346     */
347    public boolean isEnabled() {
348        try {
349            return mService.isEnabled();
350        } catch (RemoteException e) {Log.e(TAG, "", e);}
351        return false;
352    }
353
354    /**
355     * Get the current state of the local Bluetooth adapter.
356     * <p>Possible return values are
357     * {@link #STATE_OFF},
358     * {@link #STATE_TURNING_ON},
359     * {@link #STATE_ON},
360     * {@link #STATE_TURNING_OFF}.
361     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
362     *
363     * @return current state of Bluetooth adapter
364     */
365    public int getState() {
366        try {
367            return mService.getBluetoothState();
368        } catch (RemoteException e) {Log.e(TAG, "", e);}
369        return STATE_OFF;
370    }
371
372    /**
373     * Turn on the local Bluetooth adapter.
374     * <p>This powers on the underlying Bluetooth hardware, and starts all
375     * Bluetooth system services.
376     * <p>This is an asynchronous call: it will return immediately, and
377     * clients should listen for {@link #ACTION_STATE_CHANGED}
378     * to be notified of subsequent adapter state changes. If this call returns
379     * true, then the adapter state will immediately transition from {@link
380     * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time
381     * later transition to either {@link #STATE_OFF} or {@link
382     * #STATE_ON}. If this call returns false then there was an
383     * immediate problem that will prevent the adapter from being turned on -
384     * such as Airplane mode, or the adapter is already turned on.
385     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
386     *
387     * @return true to indicate adapter startup has begun, or false on
388     *         immediate error
389     */
390    public boolean enable() {
391        try {
392            return mService.enable();
393        } catch (RemoteException e) {Log.e(TAG, "", e);}
394        return false;
395    }
396
397    /**
398     * Turn off the local Bluetooth adapter.
399     * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
400     * system services, and powers down the underlying Bluetooth hardware.
401     * <p>This is an asynchronous call: it will return immediately, and
402     * clients should listen for {@link #ACTION_STATE_CHANGED}
403     * to be notified of subsequent adapter state changes. If this call returns
404     * true, then the adapter state will immediately transition from {@link
405     * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
406     * later transition to either {@link #STATE_OFF} or {@link
407     * #STATE_ON}. If this call returns false then there was an
408     * immediate problem that will prevent the adapter from being turned off -
409     * such as the adapter already being turned off.
410     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
411     *
412     * @return true to indicate adapter shutdown has begun, or false on
413     *         immediate error
414     */
415    public boolean disable() {
416        try {
417            return mService.disable(true);
418        } catch (RemoteException e) {Log.e(TAG, "", e);}
419        return false;
420    }
421
422    /**
423     * Returns the hardware address of the local Bluetooth adapter.
424     * <p>For example, "00:11:22:AA:BB:CC".
425     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
426     *
427     * @return Bluetooth hardware address as string
428     */
429    public String getAddress() {
430        try {
431            return mService.getAddress();
432        } catch (RemoteException e) {Log.e(TAG, "", e);}
433        return null;
434    }
435
436    /**
437     * Get the friendly Bluetooth name of the local Bluetooth adapter.
438     * <p>This name is visible to remote Bluetooth devices.
439     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
440     *
441     * @return the Bluetooth name, or null on error
442     */
443    public String getName() {
444        try {
445            return mService.getName();
446        } catch (RemoteException e) {Log.e(TAG, "", e);}
447        return null;
448    }
449
450    /**
451     * Set the friendly Bluetooth name of the local Bluetoth adapter.
452     * <p>This name is visible to remote Bluetooth devices.
453     * <p>Valid Bluetooth names are a maximum of 248 UTF-8 characters, however
454     * many remote devices can only display the first 40 characters, and some
455     * may be limited to just 20.
456     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
457     *
458     * @param name a valid Bluetooth name
459     * @return     true if the name was set, false otherwise
460     */
461    public boolean setName(String name) {
462        try {
463            return mService.setName(name);
464        } catch (RemoteException e) {Log.e(TAG, "", e);}
465        return false;
466    }
467
468    /**
469     * Get the current Bluetooth scan mode of the local Bluetooth adaper.
470     * <p>The Bluetooth scan mode determines if the local adapter is
471     * connectable and/or discoverable from remote Bluetooth devices.
472     * <p>Possible values are:
473     * {@link #SCAN_MODE_NONE},
474     * {@link #SCAN_MODE_CONNECTABLE},
475     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
476     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
477     *
478     * @return scan mode
479     */
480    public int getScanMode() {
481        try {
482            return mService.getScanMode();
483        } catch (RemoteException e) {Log.e(TAG, "", e);}
484        return SCAN_MODE_NONE;
485    }
486
487    /**
488     * Set the Bluetooth scan mode of the local Bluetooth adapter.
489     * <p>The Bluetooth scan mode determines if the local adapter is
490     * connectable and/or discoverable from remote Bluetooth devices.
491     * <p>For privacy reasons, discoverable mode is automatically turned off
492     * after <code>duration</code> seconds. For example, 120 seconds should be
493     * enough for a remote device to initiate and complete its discovery
494     * process.
495     * <p>Valid scan mode values are:
496     * {@link #SCAN_MODE_NONE},
497     * {@link #SCAN_MODE_CONNECTABLE},
498     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
499     * <p>Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
500     * <p>Applications cannot set the scan mode. They should use
501     * <code>startActivityForResult(
502     * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
503     * </code>instead.
504     *
505     * @param mode valid scan mode
506     * @param duration time in seconds to apply scan mode, only used for
507     *                 {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}
508     * @return     true if the scan mode was set, false otherwise
509     * @hide
510     */
511    public boolean setScanMode(int mode, int duration) {
512        try {
513            return mService.setScanMode(mode, duration);
514        } catch (RemoteException e) {Log.e(TAG, "", e);}
515        return false;
516    }
517
518    /** @hide */
519    public boolean setScanMode(int mode) {
520        return setScanMode(mode, 120);
521    }
522
523    /** @hide */
524    public int getDiscoverableTimeout() {
525        try {
526            return mService.getDiscoverableTimeout();
527        } catch (RemoteException e) {Log.e(TAG, "", e);}
528        return -1;
529    }
530
531    /** @hide */
532    public void setDiscoverableTimeout(int timeout) {
533        try {
534            mService.setDiscoverableTimeout(timeout);
535        } catch (RemoteException e) {Log.e(TAG, "", e);}
536    }
537
538    /**
539     * Start the remote device discovery process.
540     * <p>The discovery process usually involves an inquiry scan of about 12
541     * seconds, followed by a page scan of each new device to retrieve its
542     * Bluetooth name.
543     * <p>This is an asynchronous call, it will return immediately. Register
544     * for {@link #ACTION_DISCOVERY_STARTED} and {@link
545     * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the
546     * discovery starts and completes. Register for {@link
547     * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices
548     * are found.
549     * <p>Device discovery is a heavyweight procedure. New connections to
550     * remote Bluetooth devices should not be attempted while discovery is in
551     * progress, and existing connections will experience limited bandwidth
552     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
553     * discovery. Discovery is not managed by the Activity,
554     * but is run as a system service, so an application should always call
555     * {@link BluetoothAdapter#cancelDiscovery()} even if it
556     * did not directly request a discovery, just to be sure.
557     * <p>Device discovery will only find remote devices that are currently
558     * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
559     * not discoverable by default, and need to be entered into a special mode.
560     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
561     *
562     * @return true on success, false on error
563     */
564    public boolean startDiscovery() {
565        try {
566            return mService.startDiscovery();
567        } catch (RemoteException e) {Log.e(TAG, "", e);}
568        return false;
569    }
570
571    /**
572     * Cancel the current device discovery process.
573     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
574     * <p>Because discovery is a heavyweight precedure for the Bluetooth
575     * adapter, this method should always be called before attempting to connect
576     * to a remote device with {@link
577     * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by
578     * the  Activity, but is run as a system service, so an application should
579     * always call cancel discovery even if it did not directly request a
580     * discovery, just to be sure.
581     *
582     * @return true on success, false on error
583     */
584    public boolean cancelDiscovery() {
585        try {
586            mService.cancelDiscovery();
587        } catch (RemoteException e) {Log.e(TAG, "", e);}
588        return false;
589    }
590
591    /**
592     * Return true if the local Bluetooth adapter is currently in the device
593     * discovery process.
594     * <p>Device discovery is a heavyweight procedure. New connections to
595     * remote Bluetooth devices should not be attempted while discovery is in
596     * progress, and existing connections will experience limited bandwidth
597     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
598     * discovery.
599     * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
600     * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
601     * starts or completes.
602     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
603     *
604     * @return true if discovering
605     */
606    public boolean isDiscovering() {
607        try {
608            return mService.isDiscovering();
609        } catch (RemoteException e) {Log.e(TAG, "", e);}
610        return false;
611    }
612
613    /**
614     * Return the set of {@link BluetoothDevice} objects that are bonded
615     * (paired) to the local adapter.
616     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
617     *
618     * @return unmodifiable set of {@link BluetoothDevice}, or null on error
619     */
620    public Set<BluetoothDevice> getBondedDevices() {
621        try {
622            return toDeviceSet(mService.listBonds());
623        } catch (RemoteException e) {Log.e(TAG, "", e);}
624        return null;
625    }
626
627    /**
628     * Picks RFCOMM channels until none are left.
629     * Avoids reserved channels.
630     */
631    private static class RfcommChannelPicker {
632        private static final int[] RESERVED_RFCOMM_CHANNELS =  new int[] {
633            10,  // HFAG
634            11,  // HSAG
635            12,  // OPUSH
636            19,  // PBAP
637        };
638        private static LinkedList<Integer> sChannels;  // master list of non-reserved channels
639        private static Random sRandom;
640
641        private final LinkedList<Integer> mChannels;  // local list of channels left to try
642
643        private final UUID mUuid;
644
645        public RfcommChannelPicker(UUID uuid) {
646            synchronized (RfcommChannelPicker.class) {
647                if (sChannels == null) {
648                    // lazy initialization of non-reserved rfcomm channels
649                    sChannels = new LinkedList<Integer>();
650                    for (int i = 1; i <= BluetoothSocket.MAX_RFCOMM_CHANNEL; i++) {
651                        sChannels.addLast(new Integer(i));
652                    }
653                    for (int reserved : RESERVED_RFCOMM_CHANNELS) {
654                        sChannels.remove(new Integer(reserved));
655                    }
656                    sRandom = new Random();
657                }
658                mChannels = (LinkedList<Integer>)sChannels.clone();
659            }
660            mUuid = uuid;
661        }
662        /* Returns next random channel, or -1 if we're out */
663        public int nextChannel() {
664            if (mChannels.size() == 0) {
665                return -1;
666            }
667            return mChannels.remove(sRandom.nextInt(mChannels.size()));
668        }
669    }
670
671    /**
672     * Create a listening, secure RFCOMM Bluetooth socket.
673     * <p>A remote device connecting to this socket will be authenticated and
674     * communication on this socket will be encrypted.
675     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
676     * connections from a listening {@link BluetoothServerSocket}.
677     * <p>Valid RFCOMM channels are in range 1 to 30.
678     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
679     * @param channel RFCOMM channel to listen on
680     * @return a listening RFCOMM BluetoothServerSocket
681     * @throws IOException on error, for example Bluetooth not available, or
682     *                     insufficient permissions, or channel in use.
683     * @hide
684     */
685    public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
686        BluetoothServerSocket socket = new BluetoothServerSocket(
687                BluetoothSocket.TYPE_RFCOMM, true, true, channel);
688        int errno = socket.mSocket.bindListen();
689        if (errno != 0) {
690            try {
691                socket.close();
692            } catch (IOException e) {}
693            socket.mSocket.throwErrnoNative(errno);
694        }
695        return socket;
696    }
697
698    /**
699     * Create a listening, secure RFCOMM Bluetooth socket with Service Record.
700     * <p>A remote device connecting to this socket will be authenticated and
701     * communication on this socket will be encrypted.
702     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
703     * connections from a listening {@link BluetoothServerSocket}.
704     * <p>The system will assign an unused RFCOMM channel to listen on.
705     * <p>The system will also register a Service Discovery
706     * Protocol (SDP) record with the local SDP server containing the specified
707     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
708     * can use the same UUID to query our SDP server and discover which channel
709     * to connect to. This SDP record will be removed when this socket is
710     * closed, or if this application closes unexpectedly.
711     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
712     * connect to this socket from another device using the same {@link UUID}.
713     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
714     * @param name service name for SDP record
715     * @param uuid uuid for SDP record
716     * @return a listening RFCOMM BluetoothServerSocket
717     * @throws IOException on error, for example Bluetooth not available, or
718     *                     insufficient permissions, or channel in use.
719     */
720    public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
721            throws IOException {
722        RfcommChannelPicker picker = new RfcommChannelPicker(uuid);
723
724        BluetoothServerSocket socket;
725        int channel;
726        int errno;
727        while (true) {
728            channel = picker.nextChannel();
729
730            if (channel == -1) {
731                throw new IOException("No available channels");
732            }
733
734            socket = new BluetoothServerSocket(
735                    BluetoothSocket.TYPE_RFCOMM, true, true, channel);
736            errno = socket.mSocket.bindListen();
737            if (errno == 0) {
738                if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel);
739                break;  // success
740            } else if (errno == BluetoothSocket.EADDRINUSE) {
741                if (DBG) Log.d(TAG, "RFCOMM channel " + channel + " in use");
742                try {
743                    socket.close();
744                } catch (IOException e) {}
745                continue;  // try another channel
746            } else {
747                try {
748                    socket.close();
749                } catch (IOException e) {}
750                socket.mSocket.throwErrnoNative(errno);  // Exception as a result of bindListen()
751            }
752        }
753
754        int handle = -1;
755        try {
756            handle = mService.addRfcommServiceRecord(name, new ParcelUuid(uuid), channel,
757                    new Binder());
758        } catch (RemoteException e) {Log.e(TAG, "", e);}
759        if (handle == -1) {
760            try {
761                socket.close();
762            } catch (IOException e) {}
763            throw new IOException("Not able to register SDP record for " + name);
764        }
765        socket.setCloseHandler(mHandler, handle);
766        return socket;
767    }
768
769    /**
770     * Construct an unencrypted, unauthenticated, RFCOMM server socket.
771     * Call #accept to retrieve connections to this socket.
772     * @return An RFCOMM BluetoothServerSocket
773     * @throws IOException On error, for example Bluetooth not available, or
774     *                     insufficient permissions.
775     * @hide
776     */
777    public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
778        BluetoothServerSocket socket = new BluetoothServerSocket(
779                BluetoothSocket.TYPE_RFCOMM, false, false, port);
780        int errno = socket.mSocket.bindListen();
781        if (errno != 0) {
782            try {
783                socket.close();
784            } catch (IOException e) {}
785            socket.mSocket.throwErrnoNative(errno);
786        }
787        return socket;
788    }
789
790    /**
791     * Construct a SCO server socket.
792     * Call #accept to retrieve connections to this socket.
793     * @return A SCO BluetoothServerSocket
794     * @throws IOException On error, for example Bluetooth not available, or
795     *                     insufficient permissions.
796     * @hide
797     */
798    public static BluetoothServerSocket listenUsingScoOn() throws IOException {
799        BluetoothServerSocket socket = new BluetoothServerSocket(
800                BluetoothSocket.TYPE_SCO, false, false, -1);
801        int errno = socket.mSocket.bindListen();
802        if (errno != 0) {
803            try {
804                socket.close();
805            } catch (IOException e) {}
806            socket.mSocket.throwErrnoNative(errno);
807        }
808        return socket;
809    }
810
811    private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
812        Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
813        for (int i = 0; i < addresses.length; i++) {
814            devices.add(getRemoteDevice(addresses[i]));
815        }
816        return Collections.unmodifiableSet(devices);
817    }
818
819    private Handler mHandler = new Handler() {
820        public void handleMessage(Message msg) {
821            /* handle socket closing */
822            int handle = msg.what;
823            try {
824                if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle));
825                mService.removeServiceRecord(handle);
826            } catch (RemoteException e) {Log.e(TAG, "", e);}
827        }
828    };
829
830    /**
831     * Validate a Bluetooth address, such as "00:43:A8:23:10:F0"
832     * <p>Alphabetic characters must be uppercase to be valid.
833     *
834     * @param address Bluetooth address as string
835     * @return true if the address is valid, false otherwise
836     */
837    public static boolean checkBluetoothAddress(String address) {
838        if (address == null || address.length() != ADDRESS_LENGTH) {
839            return false;
840        }
841        for (int i = 0; i < ADDRESS_LENGTH; i++) {
842            char c = address.charAt(i);
843            switch (i % 3) {
844            case 0:
845            case 1:
846                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
847                    // hex character, OK
848                    break;
849                }
850                return false;
851            case 2:
852                if (c == ':') {
853                    break;  // OK
854                }
855                return false;
856            }
857        }
858        return true;
859    }
860}
861