• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.bluetooth;
18 
19 import android.annotation.SdkConstant;
20 import android.annotation.SdkConstant.SdkConstantType;
21 import android.os.Binder;
22 import android.os.Handler;
23 import android.os.IBinder;
24 import android.os.Message;
25 import android.os.ParcelUuid;
26 import android.os.RemoteException;
27 import android.os.ServiceManager;
28 import android.util.Log;
29 
30 import java.io.IOException;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.LinkedList;
34 import java.util.Random;
35 import java.util.Set;
36 import 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  */
64 public 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      */
getDefaultAdapter()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      */
BluetoothAdapter(IBluetooth service)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      */
getRemoteDevice(String address)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      */
isEnabled()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      */
getState()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      */
enable()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      */
disable()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      */
getAddress()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      */
getName()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      */
setName(String name)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      */
getScanMode()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      */
setScanMode(int mode, int duration)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 */
setScanMode(int mode)519     public boolean setScanMode(int mode) {
520         return setScanMode(mode, 120);
521     }
522 
523     /** @hide */
getDiscoverableTimeout()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 */
setDiscoverableTimeout(int timeout)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      */
startDiscovery()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      */
cancelDiscovery()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      */
isDiscovering()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      */
getBondedDevices()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 
RfcommChannelPicker(UUID uuid)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 */
nextChannel()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      */
listenUsingRfcommOn(int channel)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      */
listenUsingRfcommWithServiceRecord(String name, UUID uuid)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      */
listenUsingInsecureRfcommOn(int port)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      */
listenUsingScoOn()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 
toDeviceSet(String[] addresses)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      */
checkBluetoothAddress(String address)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