BluetoothAdapter.java revision 12835478ee687a493d1b5882e67b6725bd539c26
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.RemoteException;
22import android.util.Log;
23
24import java.io.IOException;
25import java.util.Collections;
26import java.util.Set;
27import java.util.HashSet;
28
29/**
30 * Represents the local Bluetooth adapter.
31 *
32 * <p>Use {@link android.content.Context#getSystemService} with {@link
33 * android.content.Context#BLUETOOTH_SERVICE} to get the default local
34 * Bluetooth adapter. On most Android devices there is only one local
35 * Bluetotoh adapter.
36 *
37 * <p>Use the {@link BluetoothDevice} class for operations on remote Bluetooth
38 * devices.
39 */
40public final class BluetoothAdapter {
41    private static final String TAG = "BluetoothAdapter";
42
43    /**
44     * Sentinel error value for this class. Guaranteed to not equal any other
45     * integer constant in this class. Provided as a convenience for functions
46     * that require a sentinel error value, for example:
47     * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
48     * BluetoothAdapter.ERROR)</code>
49     */
50    public static final int ERROR = Integer.MIN_VALUE;
51
52    /**
53     * Broadcast Action: The state of the local Bluetooth adapter has been
54     * changed.
55     * <p>For example, Bluetooth has been turned on or off.
56     * <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link
57     * #EXTRA_PREVIOUS_STATE} containing the new and old states
58     * respectively.
59     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
60     */
61    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
62    public static final String ACTION_STATE_CHANGED =
63            "android.bluetooth.adapter.action.STATE_CHANGED";
64
65    /**
66     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
67     * intents to request the current power state. Possible values are:
68     * {@link #STATE_OFF},
69     * {@link #STATE_TURNING_ON},
70     * {@link #STATE_ON},
71     * {@link #STATE_TURNING_OFF},
72     */
73    public static final String EXTRA_STATE =
74            "android.bluetooth.adapter.extra.STATE";
75    /**
76     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
77     * intents to request the previous power state. Possible values are:
78     * {@link #STATE_OFF},
79     * {@link #STATE_TURNING_ON},
80     * {@link #STATE_ON},
81     * {@link #STATE_TURNING_OFF},
82     */
83    public static final String EXTRA_PREVIOUS_STATE =
84            "android.bluetooth.adapter.extra.PREVIOUS_STATE";
85
86    /**
87     * Indicates the local Bluetooth adapter is off.
88     */
89    public static final int STATE_OFF = 10;
90    /**
91     * Indicates the local Bluetooth adapter is turning on. However local
92     * clients should wait for {@link #STATE_ON} before attempting to
93     * use the adapter.
94     */
95    public static final int STATE_TURNING_ON = 11;
96    /**
97     * Indicates the local Bluetooth adapter is on, and ready for use.
98     */
99    public static final int STATE_ON = 12;
100    /**
101     * Indicates the local Bluetooth adapter is turning off. Local clients
102     * should immediately attempt graceful disconnection of any remote links.
103     */
104    public static final int STATE_TURNING_OFF = 13;
105
106    /**
107     * Activity Action: Show a system activity that requests discoverable mode.
108     * <p>Discoverable mode is equivalent to {@link
109     * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see
110     * this Bluetooth adapter when they perform a discovery.
111     * <p>For privacy, Android is not by default discoverable.
112     * <p>The sender can optionally use extra field {@link
113     * #EXTRA_DISCOVERABLE_DURATION} to request the duration of
114     * discoverability. Currently the default duration is 120 seconds, and
115     * maximum duration is capped at 300 seconds for each request.
116     * <p>Notification of the result of this activity is posted using the
117     * {@link android.app.Activity#onActivityResult} callback. The
118     * <code>resultCode</code>
119     * will be the duration (in seconds) of discoverability, or a negative
120     * value if the user rejected discoverability.
121     * <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED}
122     * for global notification whenever the scan mode changes.
123     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
124     */
125    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
126    public static final String ACTION_REQUEST_DISCOVERABLE =
127            "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
128
129    /**
130     * Used as an optional int extra field in {@link
131     * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration
132     * for discoverability in seconds. The current default is 120 seconds, and
133     * requests over 300 seconds will be capped. These values could change.
134     */
135    public static final String EXTRA_DISCOVERABLE_DURATION =
136            "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
137
138    /**
139     * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter
140     * has changed.
141     * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
142     * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes
143     * respectively.
144     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
145     */
146    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
147    public static final String ACTION_SCAN_MODE_CHANGED =
148            "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
149
150    /**
151     * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
152     * intents to request the current scan mode. Possible values are:
153     * {@link #SCAN_MODE_NONE},
154     * {@link #SCAN_MODE_CONNECTABLE},
155     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
156     */
157    public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE";
158    /**
159     * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
160     * intents to request the previous scan mode. Possible values are:
161     * {@link #SCAN_MODE_NONE},
162     * {@link #SCAN_MODE_CONNECTABLE},
163     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
164     */
165    public static final String EXTRA_PREVIOUS_SCAN_MODE =
166            "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE";
167
168    /**
169     * Indicates that both inquiry scan and page scan are disabled on the local
170     * Bluetooth adapter. Therefore this device is neither discoverable
171     * nor connectable from remote Bluetooth devices.
172     */
173    public static final int SCAN_MODE_NONE = 20;
174    /**
175     * Indicates that inquiry scan is disabled, but page scan is enabled on the
176     * local Bluetooth adapter. Therefore this device is not discoverable from
177     * remote Bluetooth devices, but is connectable from remote devices that
178     * have previously discovered this device.
179     */
180    public static final int SCAN_MODE_CONNECTABLE = 21;
181    /**
182     * Indicates that both inquiry scan and page scan are enabled on the local
183     * Bluetooth adapter. Therefore this device is both discoverable and
184     * connectable from remote Bluetooth devices.
185     */
186    public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;
187
188
189    /**
190     * Broadcast Action: The local Bluetooth adapter has started the remote
191     * device discovery process.
192     * <p>This usually involves an inquiry scan of about 12 seconds, followed
193     * by a page scan of each new device to retrieve its Bluetooth name.
194     * <p>Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as
195     * remote Bluetooth devices are found.
196     * <p>Device discovery is a heavyweight procedure. New connections to
197     * remote Bluetooth devices should not be attempted while discovery is in
198     * progress, and existing connections will experience limited bandwidth
199     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
200     * discovery.
201     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
202     */
203    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
204    public static final String ACTION_DISCOVERY_STARTED =
205            "android.bluetooth.adapter.action.DISCOVERY_STARTED";
206    /**
207     * Broadcast Action: The local Bluetooth adapter has finished the device
208     * discovery process.
209     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
210     */
211    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
212    public static final String ACTION_DISCOVERY_FINISHED =
213            "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
214
215    /**
216     * Broadcast Action: The local Bluetooth adapter has changed its friendly
217     * Bluetooth name.
218     * <p>This name is visible to remote Bluetooth devices.
219     * <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing
220     * the name.
221     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
222     */
223    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
224    public static final String ACTION_LOCAL_NAME_CHANGED =
225            "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
226    /**
227     * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED}
228     * intents to request the local Bluetooth name.
229     */
230    public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME";
231
232    private static final int ADDRESS_LENGTH = 17;
233
234    private final IBluetooth mService;
235
236    /**
237     * Do not use this constructor. Use Context.getSystemService() instead.
238     * @hide
239     */
240    public BluetoothAdapter(IBluetooth service) {
241        if (service == null) {
242            throw new IllegalArgumentException("service is null");
243        }
244        mService = service;
245    }
246
247    /**
248     * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
249     * address.
250     * <p>Valid Bluetooth hardware addresses must be upper case, in a format
251     * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is
252     * available to validate a Bluetooth address.
253     * <p>A {@link BluetoothDevice} will always be returned for a valid
254     * hardware address, even if this adapter has never seen that device.
255     *
256     * @param address valid Bluetooth MAC address
257     * @throws IllegalArgumentException if address is invalid
258     */
259    public BluetoothDevice getRemoteDevice(String address) {
260        return new BluetoothDevice(address);
261    }
262
263    /**
264     * Return true if Bluetooth is currently enabled and ready for use.
265     * <p>Equivalent to:
266     * <code>getBluetoothState() == STATE_ON</code>
267     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
268     *
269     * @return true if the local adapter is turned on
270     */
271    public boolean isEnabled() {
272        try {
273            return mService.isEnabled();
274        } catch (RemoteException e) {Log.e(TAG, "", e);}
275        return false;
276    }
277
278    /**
279     * Get the current state of the local Bluetooth adapter.
280     * <p>Possible return values are
281     * {@link #STATE_OFF},
282     * {@link #STATE_TURNING_ON},
283     * {@link #STATE_ON},
284     * {@link #STATE_TURNING_OFF}.
285     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
286     *
287     * @return current state of Bluetooth adapter
288     */
289    public int getState() {
290        try {
291            return mService.getBluetoothState();
292        } catch (RemoteException e) {Log.e(TAG, "", e);}
293        return STATE_OFF;
294    }
295
296    /**
297     * Turn on the local Bluetooth adapter.
298     * <p>This powers on the underlying Bluetooth hardware, and starts all
299     * Bluetooth system services.
300     * <p>This is an asynchronous call: it will return immediately, and
301     * clients should listen for {@link #ACTION_STATE_CHANGED}
302     * to be notified of subsequent adapter state changes. If this call returns
303     * true, then the adapter state will immediately transition from {@link
304     * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time
305     * later transition to either {@link #STATE_OFF} or {@link
306     * #STATE_ON}. If this call returns false then there was an
307     * immediate problem that will prevent the adapter from being turned on -
308     * such as Airplane mode, or the adapter is already turned on.
309     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
310     *
311     * @return true to indicate adapter startup has begun, or false on
312     *         immediate error
313     */
314    public boolean enable() {
315        try {
316            return mService.enable();
317        } catch (RemoteException e) {Log.e(TAG, "", e);}
318        return false;
319    }
320
321    /**
322     * Turn off the local Bluetooth adapter.
323     * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
324     * system services, and powers down the underlying Bluetooth hardware.
325     * <p>This is an asynchronous call: it will return immediately, and
326     * clients should listen for {@link #ACTION_STATE_CHANGED}
327     * to be notified of subsequent adapter state changes. If this call returns
328     * true, then the adapter state will immediately transition from {@link
329     * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
330     * later transition to either {@link #STATE_OFF} or {@link
331     * #STATE_ON}. If this call returns false then there was an
332     * immediate problem that will prevent the adapter from being turned off -
333     * such as the adapter already being turned off.
334     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
335     *
336     * @return true to indicate adapter shutdown has begun, or false on
337     *         immediate error
338     */
339    public boolean disable() {
340        try {
341            return mService.disable(true);
342        } catch (RemoteException e) {Log.e(TAG, "", e);}
343        return false;
344    }
345
346    /**
347     * Returns the hardware address of the local Bluetooth adapter.
348     * <p>For example, "00:11:22:AA:BB:CC".
349     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
350     *
351     * @return Bluetooth hardware address as string
352     */
353    public String getAddress() {
354        try {
355            return mService.getAddress();
356        } catch (RemoteException e) {Log.e(TAG, "", e);}
357        return null;
358    }
359
360    /**
361     * Get the friendly Bluetooth name of the local Bluetooth adapter.
362     * <p>This name is visible to remote Bluetooth devices.
363     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
364     *
365     * @return the Bluetooth name, or null on error
366     */
367    public String getName() {
368        try {
369            return mService.getName();
370        } catch (RemoteException e) {Log.e(TAG, "", e);}
371        return null;
372    }
373
374    /**
375     * Set the friendly Bluetooth name of the local Bluetoth adapter.
376     * <p>This name is visible to remote Bluetooth devices.
377     * <p>Valid Bluetooth names are a maximum of 248 UTF-8 characters, however
378     * many remote devices can only display the first 40 characters, and some
379     * may be limited to just 20.
380     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
381     *
382     * @param name a valid Bluetooth name
383     * @return     true if the name was set, false otherwise
384     */
385    public boolean setName(String name) {
386        try {
387            return mService.setName(name);
388        } catch (RemoteException e) {Log.e(TAG, "", e);}
389        return false;
390    }
391
392    /**
393     * Get the current Bluetooth scan mode of the local Bluetooth adaper.
394     * <p>The Bluetooth scan mode determines if the local adapter is
395     * connectable and/or discoverable from remote Bluetooth devices.
396     * <p>Possible values are:
397     * {@link #SCAN_MODE_NONE},
398     * {@link #SCAN_MODE_CONNECTABLE},
399     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
400     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
401     *
402     * @return scan mode
403     */
404    public int getScanMode() {
405        try {
406            return mService.getScanMode();
407        } catch (RemoteException e) {Log.e(TAG, "", e);}
408        return SCAN_MODE_NONE;
409    }
410
411    /**
412     * Set the Bluetooth scan mode of the local Bluetooth adapter.
413     * <p>The Bluetooth scan mode determines if the local adapter is
414     * connectable and/or discoverable from remote Bluetooth devices.
415     * <p>For privacy reasons, discoverable mode is automatically turned off
416     * after <code>duration</code> seconds. For example, 120 seconds should be
417     * enough for a remote device to initiate and complete its discovery
418     * process.
419     * <p>Valid scan mode values are:
420     * {@link #SCAN_MODE_NONE},
421     * {@link #SCAN_MODE_CONNECTABLE},
422     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
423     * <p>Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
424     * <p>Applications cannot set the scan mode. They should use
425     * <code>startActivityForResult(
426     * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
427     * </code>instead.
428     *
429     * @param mode valid scan mode
430     * @param duration time in seconds to apply scan mode, only used for
431     *                 {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}
432     * @return     true if the scan mode was set, false otherwise
433     * @hide
434     */
435    public boolean setScanMode(int mode, int duration) {
436        try {
437            return mService.setScanMode(mode, duration);
438        } catch (RemoteException e) {Log.e(TAG, "", e);}
439        return false;
440    }
441
442    /** @hide */
443    public boolean setScanMode(int mode) {
444        return setScanMode(mode, 120);
445    }
446
447    /** @hide */
448    public int getDiscoverableTimeout() {
449        try {
450            return mService.getDiscoverableTimeout();
451        } catch (RemoteException e) {Log.e(TAG, "", e);}
452        return -1;
453    }
454
455    /** @hide */
456    public void setDiscoverableTimeout(int timeout) {
457        try {
458            mService.setDiscoverableTimeout(timeout);
459        } catch (RemoteException e) {Log.e(TAG, "", e);}
460    }
461
462    /**
463     * Start the remote device discovery process.
464     * <p>The discovery process usually involves an inquiry scan of about 12
465     * seconds, followed by a page scan of each new device to retrieve its
466     * Bluetooth name.
467     * <p>This is an asynchronous call, it will return immediately. Register
468     * for {@link #ACTION_DISCOVERY_STARTED} and {@link
469     * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the
470     * discovery starts and completes. Register for {@link
471     * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices
472     * are found.
473     * <p>Device discovery is a heavyweight procedure. New connections to
474     * remote Bluetooth devices should not be attempted while discovery is in
475     * progress, and existing connections will experience limited bandwidth
476     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
477     * discovery.
478     * <p>Device discovery will only find remote devices that are currently
479     * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
480     * not discoverable by default, and need to be entered into a special mode.
481     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
482     *
483     * @return true on success, false on error
484     */
485    public boolean startDiscovery() {
486        try {
487            return mService.startDiscovery();
488        } catch (RemoteException e) {Log.e(TAG, "", e);}
489        return false;
490    }
491
492    /**
493     * Cancel the current device discovery process.
494     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
495     *
496     * @return true on success, false on error
497     */
498    public boolean cancelDiscovery() {
499        try {
500            mService.cancelDiscovery();
501        } catch (RemoteException e) {Log.e(TAG, "", e);}
502        return false;
503    }
504
505    /**
506     * Return true if the local Bluetooth adapter is currently in the device
507     * discovery process.
508     * <p>Device discovery is a heavyweight procedure. New connections to
509     * remote Bluetooth devices should not be attempted while discovery is in
510     * progress, and existing connections will experience limited bandwidth
511     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
512     * discovery.
513     * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
514     * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
515     * starts or completes.
516     *
517     * @return true if discovering
518     */
519    public boolean isDiscovering() {
520        try {
521            return mService.isDiscovering();
522        } catch (RemoteException e) {Log.e(TAG, "", e);}
523        return false;
524    }
525
526    /**
527     * Return the set of {@link BluetoothDevice} objects that are bonded
528     * (paired) to the local adapter.
529     *
530     * @return unmodifiable set of {@link BluetoothDevice}, or null on error
531     */
532    public Set<BluetoothDevice> getBondedDevices() {
533        try {
534            return toDeviceSet(mService.listBonds());
535        } catch (RemoteException e) {Log.e(TAG, "", e);}
536        return null;
537    }
538
539    /**
540     * Create a listening, secure RFCOMM Bluetooth socket.
541     * <p>A remote device connecting to this socket will be authenticated and
542     * communication on this socket will be encrypted.
543     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
544     * connections to listening {@link BluetoothServerSocket}.
545     * <p>Valid RFCOMM channels are in range 1 to 30.
546     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
547     * @param channel RFCOMM channel to listen on
548     * @return a listening RFCOMM BluetoothServerSocket
549     * @throws IOException on error, for example Bluetooth not available, or
550     *                     insufficient permissions, or channel in use.
551     */
552    public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
553        BluetoothServerSocket socket = new BluetoothServerSocket(
554                BluetoothSocket.TYPE_RFCOMM, true, true, channel);
555        try {
556            socket.mSocket.bindListen();
557        } catch (IOException e) {
558            try {
559                socket.close();
560            } catch (IOException e2) { }
561            throw e;
562        }
563        return socket;
564    }
565
566    /**
567     * Construct an unencrypted, unauthenticated, RFCOMM server socket.
568     * Call #accept to retrieve connections to this socket.
569     * @return An RFCOMM BluetoothServerSocket
570     * @throws IOException On error, for example Bluetooth not available, or
571     *                     insufficient permissions.
572     * @hide
573     */
574    public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
575        BluetoothServerSocket socket = new BluetoothServerSocket(
576                BluetoothSocket.TYPE_RFCOMM, false, false, port);
577        try {
578            socket.mSocket.bindListen();
579        } catch (IOException e) {
580            try {
581                socket.close();
582            } catch (IOException e2) { }
583            throw e;
584        }
585        return socket;
586    }
587
588    /**
589     * Construct a SCO server socket.
590     * Call #accept to retrieve connections to this socket.
591     * @return A SCO BluetoothServerSocket
592     * @throws IOException On error, for example Bluetooth not available, or
593     *                     insufficient permissions.
594     * @hide
595     */
596    public static BluetoothServerSocket listenUsingScoOn() throws IOException {
597        BluetoothServerSocket socket = new BluetoothServerSocket(
598                BluetoothSocket.TYPE_SCO, false, false, -1);
599        try {
600            socket.mSocket.bindListen();
601        } catch (IOException e) {
602            try {
603                socket.close();
604            } catch (IOException e2) { }
605            throw e;
606        }
607        return socket;
608    }
609
610    private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
611        Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
612        for (int i = 0; i < addresses.length; i++) {
613            devices.add(getRemoteDevice(addresses[i]));
614        }
615        return Collections.unmodifiableSet(devices);
616    }
617
618    /**
619     * Validate a Bluetooth address, such as "00:43:A8:23:10:F0"
620     * <p>Alphabetic characters must be uppercase to be valid.
621     *
622     * @param address Bluetooth address as string
623     * @return true if the address is valid, false otherwise
624     */
625    public static boolean checkBluetoothAddress(String address) {
626        if (address == null || address.length() != ADDRESS_LENGTH) {
627            return false;
628        }
629        for (int i = 0; i < ADDRESS_LENGTH; i++) {
630            char c = address.charAt(i);
631            switch (i % 3) {
632            case 0:
633            case 1:
634                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
635                    // hex character, OK
636                    break;
637                }
638                return false;
639            case 2:
640                if (c == ':') {
641                    break;  // OK
642                }
643                return false;
644            }
645        }
646        return true;
647    }
648}
649