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