Home | History | Annotate | Download | only in bluetooth
      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      */
    301     public static synchronized BluetoothAdapter getDefaultAdapter() {
    302         if (sAdapter == null) {
    303             IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
    304             if (b != null) {
    305                 IBluetooth service = IBluetooth.Stub.asInterface(b);
    306                 sAdapter = new BluetoothAdapter(service);
    307             }
    308         }
    309         return sAdapter;
    310     }
    311 
    312     /**
    313      * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
    314      * @hide
    315      */
    316     public BluetoothAdapter(IBluetooth service) {
    317         if (service == null) {
    318             throw new IllegalArgumentException("service is null");
    319         }
    320         mService = service;
    321     }
    322 
    323     /**
    324      * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
    325      * address.
    326      * <p>Valid Bluetooth hardware addresses must be upper case, in a format
    327      * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is
    328      * available to validate a Bluetooth address.
    329      * <p>A {@link BluetoothDevice} will always be returned for a valid
    330      * hardware address, even if this adapter has never seen that device.
    331      *
    332      * @param address valid Bluetooth MAC address
    333      * @throws IllegalArgumentException if address is invalid
    334      */
    335     public BluetoothDevice getRemoteDevice(String address) {
    336         return new BluetoothDevice(address);
    337     }
    338 
    339     /**
    340      * Return true if Bluetooth is currently enabled and ready for use.
    341      * <p>Equivalent to:
    342      * <code>getBluetoothState() == STATE_ON</code>
    343      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
    344      *
    345      * @return true if the local adapter is turned on
    346      */
    347     public boolean isEnabled() {
    348         try {
    349             return mService.isEnabled();
    350         } catch (RemoteException e) {Log.e(TAG, "", e);}
    351         return false;
    352     }
    353 
    354     /**
    355      * Get the current state of the local Bluetooth adapter.
    356      * <p>Possible return values are
    357      * {@link #STATE_OFF},
    358      * {@link #STATE_TURNING_ON},
    359      * {@link #STATE_ON},
    360      * {@link #STATE_TURNING_OFF}.
    361      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
    362      *
    363      * @return current state of Bluetooth adapter
    364      */
    365     public int getState() {
    366         try {
    367             return mService.getBluetoothState();
    368         } catch (RemoteException e) {Log.e(TAG, "", e);}
    369         return STATE_OFF;
    370     }
    371 
    372     /**
    373      * Turn on the local Bluetooth adapter&mdash;do not use without explicit
    374      * user action to turn on Bluetooth.
    375      * <p>This powers on the underlying Bluetooth hardware, and starts all
    376      * Bluetooth system services.
    377      * <p class="caution"><strong>Bluetooth should never be enabled without
    378      * direct user consent</strong>. If you want to turn on Bluetooth in order
    379      * to create a wireless connection, you should use the {@link
    380      * #ACTION_REQUEST_ENABLE} Intent, which will raise a dialog that requests
    381      * user permission to turn on Bluetooth. The {@link #enable()} method is
    382      * provided only for applications that include a user interface for changing
    383      * system settings, such as a "power manager" app.</p>
    384      * <p>This is an asynchronous call: it will return immediately, and
    385      * clients should listen for {@link #ACTION_STATE_CHANGED}
    386      * to be notified of subsequent adapter state changes. If this call returns
    387      * true, then the adapter state will immediately transition from {@link
    388      * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time
    389      * later transition to either {@link #STATE_OFF} or {@link
    390      * #STATE_ON}. If this call returns false then there was an
    391      * immediate problem that will prevent the adapter from being turned on -
    392      * such as Airplane mode, or the adapter is already turned on.
    393      * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    394      * permission
    395      *
    396      * @return true to indicate adapter startup has begun, or false on
    397      *         immediate error
    398      */
    399     public boolean enable() {
    400         try {
    401             return mService.enable();
    402         } catch (RemoteException e) {Log.e(TAG, "", e);}
    403         return false;
    404     }
    405 
    406     /**
    407      * Turn off the local Bluetooth adapter&mdash;do not use without explicit
    408      * user action to turn off Bluetooth.
    409      * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
    410      * system services, and powers down the underlying Bluetooth hardware.
    411      * <p class="caution"><strong>Bluetooth should never be disbled without
    412      * direct user consent</strong>. The {@link #disable()} method is
    413      * provided only for applications that include a user interface for changing
    414      * system settings, such as a "power manager" app.</p>
    415      * <p>This is an asynchronous call: it will return immediately, and
    416      * clients should listen for {@link #ACTION_STATE_CHANGED}
    417      * to be notified of subsequent adapter state changes. If this call returns
    418      * true, then the adapter state will immediately transition from {@link
    419      * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
    420      * later transition to either {@link #STATE_OFF} or {@link
    421      * #STATE_ON}. If this call returns false then there was an
    422      * immediate problem that will prevent the adapter from being turned off -
    423      * such as the adapter already being turned off.
    424      * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    425      * permission
    426      *
    427      * @return true to indicate adapter shutdown has begun, or false on
    428      *         immediate error
    429      */
    430     public boolean disable() {
    431         try {
    432             return mService.disable(true);
    433         } catch (RemoteException e) {Log.e(TAG, "", e);}
    434         return false;
    435     }
    436 
    437     /**
    438      * Returns the hardware address of the local Bluetooth adapter.
    439      * <p>For example, "00:11:22:AA:BB:CC".
    440      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
    441      *
    442      * @return Bluetooth hardware address as string
    443      */
    444     public String getAddress() {
    445         try {
    446             return mService.getAddress();
    447         } catch (RemoteException e) {Log.e(TAG, "", e);}
    448         return null;
    449     }
    450 
    451     /**
    452      * Get the friendly Bluetooth name of the local Bluetooth adapter.
    453      * <p>This name is visible to remote Bluetooth devices.
    454      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
    455      *
    456      * @return the Bluetooth name, or null on error
    457      */
    458     public String getName() {
    459         try {
    460             return mService.getName();
    461         } catch (RemoteException e) {Log.e(TAG, "", e);}
    462         return null;
    463     }
    464 
    465     /**
    466      * Set the friendly Bluetooth name of the local Bluetoth adapter.
    467      * <p>This name is visible to remote Bluetooth devices.
    468      * <p>Valid Bluetooth names are a maximum of 248 UTF-8 characters, however
    469      * many remote devices can only display the first 40 characters, and some
    470      * may be limited to just 20.
    471      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    472      *
    473      * @param name a valid Bluetooth name
    474      * @return     true if the name was set, false otherwise
    475      */
    476     public boolean setName(String name) {
    477         try {
    478             return mService.setName(name);
    479         } catch (RemoteException e) {Log.e(TAG, "", e);}
    480         return false;
    481     }
    482 
    483     /**
    484      * Get the current Bluetooth scan mode of the local Bluetooth adaper.
    485      * <p>The Bluetooth scan mode determines if the local adapter is
    486      * connectable and/or discoverable from remote Bluetooth devices.
    487      * <p>Possible values are:
    488      * {@link #SCAN_MODE_NONE},
    489      * {@link #SCAN_MODE_CONNECTABLE},
    490      * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
    491      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
    492      *
    493      * @return scan mode
    494      */
    495     public int getScanMode() {
    496         try {
    497             return mService.getScanMode();
    498         } catch (RemoteException e) {Log.e(TAG, "", e);}
    499         return SCAN_MODE_NONE;
    500     }
    501 
    502     /**
    503      * Set the Bluetooth scan mode of the local Bluetooth adapter.
    504      * <p>The Bluetooth scan mode determines if the local adapter is
    505      * connectable and/or discoverable from remote Bluetooth devices.
    506      * <p>For privacy reasons, discoverable mode is automatically turned off
    507      * after <code>duration</code> seconds. For example, 120 seconds should be
    508      * enough for a remote device to initiate and complete its discovery
    509      * process.
    510      * <p>Valid scan mode values are:
    511      * {@link #SCAN_MODE_NONE},
    512      * {@link #SCAN_MODE_CONNECTABLE},
    513      * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
    514      * <p>Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
    515      * <p>Applications cannot set the scan mode. They should use
    516      * <code>startActivityForResult(
    517      * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
    518      * </code>instead.
    519      *
    520      * @param mode valid scan mode
    521      * @param duration time in seconds to apply scan mode, only used for
    522      *                 {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}
    523      * @return     true if the scan mode was set, false otherwise
    524      * @hide
    525      */
    526     public boolean setScanMode(int mode, int duration) {
    527         try {
    528             return mService.setScanMode(mode, duration);
    529         } catch (RemoteException e) {Log.e(TAG, "", e);}
    530         return false;
    531     }
    532 
    533     /** @hide */
    534     public boolean setScanMode(int mode) {
    535         return setScanMode(mode, 120);
    536     }
    537 
    538     /** @hide */
    539     public int getDiscoverableTimeout() {
    540         try {
    541             return mService.getDiscoverableTimeout();
    542         } catch (RemoteException e) {Log.e(TAG, "", e);}
    543         return -1;
    544     }
    545 
    546     /** @hide */
    547     public void setDiscoverableTimeout(int timeout) {
    548         try {
    549             mService.setDiscoverableTimeout(timeout);
    550         } catch (RemoteException e) {Log.e(TAG, "", e);}
    551     }
    552 
    553     /**
    554      * Start the remote device discovery process.
    555      * <p>The discovery process usually involves an inquiry scan of about 12
    556      * seconds, followed by a page scan of each new device to retrieve its
    557      * Bluetooth name.
    558      * <p>This is an asynchronous call, it will return immediately. Register
    559      * for {@link #ACTION_DISCOVERY_STARTED} and {@link
    560      * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the
    561      * discovery starts and completes. Register for {@link
    562      * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices
    563      * are found.
    564      * <p>Device discovery is a heavyweight procedure. New connections to
    565      * remote Bluetooth devices should not be attempted while discovery is in
    566      * progress, and existing connections will experience limited bandwidth
    567      * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
    568      * discovery. Discovery is not managed by the Activity,
    569      * but is run as a system service, so an application should always call
    570      * {@link BluetoothAdapter#cancelDiscovery()} even if it
    571      * did not directly request a discovery, just to be sure.
    572      * <p>Device discovery will only find remote devices that are currently
    573      * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
    574      * not discoverable by default, and need to be entered into a special mode.
    575      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
    576      *
    577      * @return true on success, false on error
    578      */
    579     public boolean startDiscovery() {
    580         try {
    581             return mService.startDiscovery();
    582         } catch (RemoteException e) {Log.e(TAG, "", e);}
    583         return false;
    584     }
    585 
    586     /**
    587      * Cancel the current device discovery process.
    588      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
    589      * <p>Because discovery is a heavyweight precedure for the Bluetooth
    590      * adapter, this method should always be called before attempting to connect
    591      * to a remote device with {@link
    592      * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by
    593      * the  Activity, but is run as a system service, so an application should
    594      * always call cancel discovery even if it did not directly request a
    595      * discovery, just to be sure.
    596      *
    597      * @return true on success, false on error
    598      */
    599     public boolean cancelDiscovery() {
    600         try {
    601             mService.cancelDiscovery();
    602         } catch (RemoteException e) {Log.e(TAG, "", e);}
    603         return false;
    604     }
    605 
    606     /**
    607      * Return true if the local Bluetooth adapter is currently in the device
    608      * discovery process.
    609      * <p>Device discovery is a heavyweight procedure. New connections to
    610      * remote Bluetooth devices should not be attempted while discovery is in
    611      * progress, and existing connections will experience limited bandwidth
    612      * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
    613      * discovery.
    614      * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
    615      * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
    616      * starts or completes.
    617      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
    618      *
    619      * @return true if discovering
    620      */
    621     public boolean isDiscovering() {
    622         try {
    623             return mService.isDiscovering();
    624         } catch (RemoteException e) {Log.e(TAG, "", e);}
    625         return false;
    626     }
    627 
    628     /**
    629      * Return the set of {@link BluetoothDevice} objects that are bonded
    630      * (paired) to the local adapter.
    631      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
    632      *
    633      * @return unmodifiable set of {@link BluetoothDevice}, or null on error
    634      */
    635     public Set<BluetoothDevice> getBondedDevices() {
    636         try {
    637             return toDeviceSet(mService.listBonds());
    638         } catch (RemoteException e) {Log.e(TAG, "", e);}
    639         return null;
    640     }
    641 
    642     /**
    643      * Picks RFCOMM channels until none are left.
    644      * Avoids reserved channels.
    645      */
    646     private static class RfcommChannelPicker {
    647         private static final int[] RESERVED_RFCOMM_CHANNELS =  new int[] {
    648             10,  // HFAG
    649             11,  // HSAG
    650             12,  // OPUSH
    651             19,  // PBAP
    652         };
    653         private static LinkedList<Integer> sChannels;  // master list of non-reserved channels
    654         private static Random sRandom;
    655 
    656         private final LinkedList<Integer> mChannels;  // local list of channels left to try
    657 
    658         private final UUID mUuid;
    659 
    660         public RfcommChannelPicker(UUID uuid) {
    661             synchronized (RfcommChannelPicker.class) {
    662                 if (sChannels == null) {
    663                     // lazy initialization of non-reserved rfcomm channels
    664                     sChannels = new LinkedList<Integer>();
    665                     for (int i = 1; i <= BluetoothSocket.MAX_RFCOMM_CHANNEL; i++) {
    666                         sChannels.addLast(new Integer(i));
    667                     }
    668                     for (int reserved : RESERVED_RFCOMM_CHANNELS) {
    669                         sChannels.remove(new Integer(reserved));
    670                     }
    671                     sRandom = new Random();
    672                 }
    673                 mChannels = (LinkedList<Integer>)sChannels.clone();
    674             }
    675             mUuid = uuid;
    676         }
    677         /* Returns next random channel, or -1 if we're out */
    678         public int nextChannel() {
    679             if (mChannels.size() == 0) {
    680                 return -1;
    681             }
    682             return mChannels.remove(sRandom.nextInt(mChannels.size()));
    683         }
    684     }
    685 
    686     /**
    687      * Create a listening, secure RFCOMM Bluetooth socket.
    688      * <p>A remote device connecting to this socket will be authenticated and
    689      * communication on this socket will be encrypted.
    690      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
    691      * connections from a listening {@link BluetoothServerSocket}.
    692      * <p>Valid RFCOMM channels are in range 1 to 30.
    693      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    694      * @param channel RFCOMM channel to listen on
    695      * @return a listening RFCOMM BluetoothServerSocket
    696      * @throws IOException on error, for example Bluetooth not available, or
    697      *                     insufficient permissions, or channel in use.
    698      * @hide
    699      */
    700     public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
    701         BluetoothServerSocket socket = new BluetoothServerSocket(
    702                 BluetoothSocket.TYPE_RFCOMM, true, true, channel);
    703         int errno = socket.mSocket.bindListen();
    704         if (errno != 0) {
    705             try {
    706                 socket.close();
    707             } catch (IOException e) {}
    708             socket.mSocket.throwErrnoNative(errno);
    709         }
    710         return socket;
    711     }
    712 
    713     /**
    714      * Create a listening, secure RFCOMM Bluetooth socket with Service Record.
    715      * <p>A remote device connecting to this socket will be authenticated and
    716      * communication on this socket will be encrypted.
    717      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
    718      * connections from a listening {@link BluetoothServerSocket}.
    719      * <p>The system will assign an unused RFCOMM channel to listen on.
    720      * <p>The system will also register a Service Discovery
    721      * Protocol (SDP) record with the local SDP server containing the specified
    722      * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
    723      * can use the same UUID to query our SDP server and discover which channel
    724      * to connect to. This SDP record will be removed when this socket is
    725      * closed, or if this application closes unexpectedly.
    726      * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
    727      * connect to this socket from another device using the same {@link UUID}.
    728      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
    729      * @param name service name for SDP record
    730      * @param uuid uuid for SDP record
    731      * @return a listening RFCOMM BluetoothServerSocket
    732      * @throws IOException on error, for example Bluetooth not available, or
    733      *                     insufficient permissions, or channel in use.
    734      */
    735     public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
    736             throws IOException {
    737         RfcommChannelPicker picker = new RfcommChannelPicker(uuid);
    738 
    739         BluetoothServerSocket socket;
    740         int channel;
    741         int errno;
    742         while (true) {
    743             channel = picker.nextChannel();
    744 
    745             if (channel == -1) {
    746                 throw new IOException("No available channels");
    747             }
    748 
    749             socket = new BluetoothServerSocket(
    750                     BluetoothSocket.TYPE_RFCOMM, true, true, channel);
    751             errno = socket.mSocket.bindListen();
    752             if (errno == 0) {
    753                 if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel);
    754                 break;  // success
    755             } else if (errno == BluetoothSocket.EADDRINUSE) {
    756                 if (DBG) Log.d(TAG, "RFCOMM channel " + channel + " in use");
    757                 try {
    758                     socket.close();
    759                 } catch (IOException e) {}
    760                 continue;  // try another channel
    761             } else {
    762                 try {
    763                     socket.close();
    764                 } catch (IOException e) {}
    765                 socket.mSocket.throwErrnoNative(errno);  // Exception as a result of bindListen()
    766             }
    767         }
    768 
    769         int handle = -1;
    770         try {
    771             handle = mService.addRfcommServiceRecord(name, new ParcelUuid(uuid), channel,
    772                     new Binder());
    773         } catch (RemoteException e) {Log.e(TAG, "", e);}
    774         if (handle == -1) {
    775             try {
    776                 socket.close();
    777             } catch (IOException e) {}
    778             throw new IOException("Not able to register SDP record for " + name);
    779         }
    780         socket.setCloseHandler(mHandler, handle);
    781         return socket;
    782     }
    783 
    784     /**
    785      * Construct an unencrypted, unauthenticated, RFCOMM server socket.
    786      * Call #accept to retrieve connections to this socket.
    787      * @return An RFCOMM BluetoothServerSocket
    788      * @throws IOException On error, for example Bluetooth not available, or
    789      *                     insufficient permissions.
    790      * @hide
    791      */
    792     public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
    793         BluetoothServerSocket socket = new BluetoothServerSocket(
    794                 BluetoothSocket.TYPE_RFCOMM, false, false, port);
    795         int errno = socket.mSocket.bindListen();
    796         if (errno != 0) {
    797             try {
    798                 socket.close();
    799             } catch (IOException e) {}
    800             socket.mSocket.throwErrnoNative(errno);
    801         }
    802         return socket;
    803     }
    804 
    805     /**
    806      * Construct a SCO server socket.
    807      * Call #accept to retrieve connections to this socket.
    808      * @return A SCO BluetoothServerSocket
    809      * @throws IOException On error, for example Bluetooth not available, or
    810      *                     insufficient permissions.
    811      * @hide
    812      */
    813     public static BluetoothServerSocket listenUsingScoOn() throws IOException {
    814         BluetoothServerSocket socket = new BluetoothServerSocket(
    815                 BluetoothSocket.TYPE_SCO, false, false, -1);
    816         int errno = socket.mSocket.bindListen();
    817         if (errno != 0) {
    818             try {
    819                 socket.close();
    820             } catch (IOException e) {}
    821             socket.mSocket.throwErrnoNative(errno);
    822         }
    823         return socket;
    824     }
    825 
    826     private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
    827         Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
    828         for (int i = 0; i < addresses.length; i++) {
    829             devices.add(getRemoteDevice(addresses[i]));
    830         }
    831         return Collections.unmodifiableSet(devices);
    832     }
    833 
    834     private Handler mHandler = new Handler() {
    835         public void handleMessage(Message msg) {
    836             /* handle socket closing */
    837             int handle = msg.what;
    838             try {
    839                 if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle));
    840                 mService.removeServiceRecord(handle);
    841             } catch (RemoteException e) {Log.e(TAG, "", e);}
    842         }
    843     };
    844 
    845     /**
    846      * Validate a Bluetooth address, such as "00:43:A8:23:10:F0"
    847      * <p>Alphabetic characters must be uppercase to be valid.
    848      *
    849      * @param address Bluetooth address as string
    850      * @return true if the address is valid, false otherwise
    851      */
    852     public static boolean checkBluetoothAddress(String address) {
    853         if (address == null || address.length() != ADDRESS_LENGTH) {
    854             return false;
    855         }
    856         for (int i = 0; i < ADDRESS_LENGTH; i++) {
    857             char c = address.charAt(i);
    858             switch (i % 3) {
    859             case 0:
    860             case 1:
    861                 if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
    862                     // hex character, OK
    863                     break;
    864                 }
    865                 return false;
    866             case 2:
    867                 if (c == ':') {
    868                     break;  // OK
    869                 }
    870                 return false;
    871             }
    872         }
    873         return true;
    874     }
    875 }
    876