1/*
2 * Copyright (C) 2009-2016 The Android Open Source Project
3 * Copyright (C) 2015 Samsung LSI
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package android.bluetooth;
19
20import android.Manifest;
21import android.annotation.IntDef;
22import android.annotation.RequiresPermission;
23import android.annotation.SdkConstant;
24import android.annotation.SdkConstant.SdkConstantType;
25import android.annotation.SystemApi;
26import android.bluetooth.le.BluetoothLeAdvertiser;
27import android.bluetooth.le.BluetoothLeScanner;
28import android.bluetooth.le.ScanCallback;
29import android.bluetooth.le.ScanFilter;
30import android.bluetooth.le.ScanRecord;
31import android.bluetooth.le.ScanResult;
32import android.bluetooth.le.ScanSettings;
33import android.content.Context;
34import android.os.BatteryStats;
35import android.os.Binder;
36import android.os.IBinder;
37import android.os.ParcelUuid;
38import android.os.RemoteException;
39import android.os.ResultReceiver;
40import android.os.ServiceManager;
41import android.os.SynchronousResultReceiver;
42import android.os.SystemProperties;
43import android.util.Log;
44import android.util.Pair;
45
46import java.io.IOException;
47import java.lang.annotation.Retention;
48import java.lang.annotation.RetentionPolicy;
49import java.util.ArrayList;
50import java.util.Arrays;
51import java.util.Collections;
52import java.util.HashMap;
53import java.util.HashSet;
54import java.util.List;
55import java.util.Locale;
56import java.util.Map;
57import java.util.Set;
58import java.util.UUID;
59import java.util.concurrent.TimeoutException;
60import java.util.concurrent.locks.ReentrantReadWriteLock;
61
62/**
63 * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
64 * lets you perform fundamental Bluetooth tasks, such as initiate
65 * device discovery, query a list of bonded (paired) devices,
66 * instantiate a {@link BluetoothDevice} using a known MAC address, and create
67 * a {@link BluetoothServerSocket} to listen for connection requests from other
68 * devices, and start a scan for Bluetooth LE devices.
69 *
70 * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth
71 * adapter, when running on JELLY_BEAN_MR1 and below, call the
72 * static {@link #getDefaultAdapter} method; when running on JELLY_BEAN_MR2 and
73 * higher, call {@link BluetoothManager#getAdapter}.
74 * Fundamentally, this is your starting point for all
75 * Bluetooth actions. Once you have the local adapter, you can get a set of
76 * {@link BluetoothDevice} objects representing all paired devices with
77 * {@link #getBondedDevices()}; start device discovery with
78 * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
79 * listen for incoming connection requests with
80 * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}; or start a scan for
81 * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
82 *
83 * <p>This class is thread safe.
84 *
85 * <p class="note"><strong>Note:</strong>
86 * Most methods require the {@link android.Manifest.permission#BLUETOOTH}
87 * permission and some also require the
88 * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
89 *
90 * <div class="special reference">
91 * <h3>Developer Guides</h3>
92 * <p>
93 *  For more information about using Bluetooth, read the <a href=
94 * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
95 * guide.
96 * </p>
97 * </div>
98 *
99 * {@see BluetoothDevice}
100 * {@see BluetoothServerSocket}
101 */
102public final class BluetoothAdapter {
103    private static final String TAG = "BluetoothAdapter";
104    private static final boolean DBG = true;
105    private static final boolean VDBG = false;
106
107    /**
108     * Default MAC address reported to a client that does not have the
109     * android.permission.LOCAL_MAC_ADDRESS permission.
110     *
111     * @hide
112     */
113    public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
114
115    /**
116     * Sentinel error value for this class. Guaranteed to not equal any other
117     * integer constant in this class. Provided as a convenience for functions
118     * that require a sentinel error value, for example:
119     * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
120     * BluetoothAdapter.ERROR)</code>
121     */
122    public static final int ERROR = Integer.MIN_VALUE;
123
124    /**
125     * Broadcast Action: The state of the local Bluetooth adapter has been
126     * changed.
127     * <p>For example, Bluetooth has been turned on or off.
128     * <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link
129     * #EXTRA_PREVIOUS_STATE} containing the new and old states
130     * respectively.
131     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
132     */
133    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
134    public static final String ACTION_STATE_CHANGED =
135            "android.bluetooth.adapter.action.STATE_CHANGED";
136
137    /**
138     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
139     * intents to request the current power state. Possible values are:
140     * {@link #STATE_OFF},
141     * {@link #STATE_TURNING_ON},
142     * {@link #STATE_ON},
143     * {@link #STATE_TURNING_OFF},
144     */
145    public static final String EXTRA_STATE =
146            "android.bluetooth.adapter.extra.STATE";
147    /**
148     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
149     * intents to request the previous power state. Possible values are:
150     * {@link #STATE_OFF},
151     * {@link #STATE_TURNING_ON},
152     * {@link #STATE_ON},
153     * {@link #STATE_TURNING_OFF}
154     */
155    public static final String EXTRA_PREVIOUS_STATE =
156            "android.bluetooth.adapter.extra.PREVIOUS_STATE";
157
158    /** @hide */
159    @IntDef({STATE_OFF, STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, STATE_BLE_TURNING_ON,
160            STATE_BLE_ON, STATE_BLE_TURNING_OFF})
161    @Retention(RetentionPolicy.SOURCE)
162    public @interface AdapterState {}
163
164    /**
165     * Indicates the local Bluetooth adapter is off.
166     */
167    public static final int STATE_OFF = 10;
168    /**
169     * Indicates the local Bluetooth adapter is turning on. However local
170     * clients should wait for {@link #STATE_ON} before attempting to
171     * use the adapter.
172     */
173    public static final int STATE_TURNING_ON = 11;
174    /**
175     * Indicates the local Bluetooth adapter is on, and ready for use.
176     */
177    public static final int STATE_ON = 12;
178    /**
179     * Indicates the local Bluetooth adapter is turning off. Local clients
180     * should immediately attempt graceful disconnection of any remote links.
181     */
182    public static final int STATE_TURNING_OFF = 13;
183
184    /**
185     * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on.
186     * @hide
187     */
188    public static final int STATE_BLE_TURNING_ON = 14;
189
190    /**
191     * Indicates the local Bluetooth adapter is in LE only mode.
192     * @hide
193     */
194    public static final int STATE_BLE_ON = 15;
195
196    /**
197     * Indicates the local Bluetooth adapter is turning off LE only mode.
198     * @hide
199     */
200    public static final int STATE_BLE_TURNING_OFF = 16;
201
202    /**
203     * Activity Action: Show a system activity that requests discoverable mode.
204     * This activity will also request the user to turn on Bluetooth if it
205     * is not currently enabled.
206     * <p>Discoverable mode is equivalent to {@link
207     * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see
208     * this Bluetooth adapter when they perform a discovery.
209     * <p>For privacy, Android is not discoverable by default.
210     * <p>The sender of this Intent can optionally use extra field {@link
211     * #EXTRA_DISCOVERABLE_DURATION} to request the duration of
212     * discoverability. Currently the default duration is 120 seconds, and
213     * maximum duration is capped at 300 seconds for each request.
214     * <p>Notification of the result of this activity is posted using the
215     * {@link android.app.Activity#onActivityResult} callback. The
216     * <code>resultCode</code>
217     * will be the duration (in seconds) of discoverability or
218     * {@link android.app.Activity#RESULT_CANCELED} if the user rejected
219     * discoverability or an error has occurred.
220     * <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED}
221     * for global notification whenever the scan mode changes. For example, an
222     * application can be notified when the device has ended discoverability.
223     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
224     */
225    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
226    public static final String ACTION_REQUEST_DISCOVERABLE =
227            "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
228
229    /**
230     * Used as an optional int extra field in {@link
231     * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration
232     * for discoverability in seconds. The current default is 120 seconds, and
233     * requests over 300 seconds will be capped. These values could change.
234     */
235    public static final String EXTRA_DISCOVERABLE_DURATION =
236            "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
237
238    /**
239     * Activity Action: Show a system activity that allows the user to turn on
240     * Bluetooth.
241     * <p>This system activity will return once Bluetooth has completed turning
242     * on, or the user has decided not to turn Bluetooth on.
243     * <p>Notification of the result of this activity is posted using the
244     * {@link android.app.Activity#onActivityResult} callback. The
245     * <code>resultCode</code>
246     * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been
247     * turned on or {@link android.app.Activity#RESULT_CANCELED} if the user
248     * has rejected the request or an error has occurred.
249     * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
250     * for global notification whenever Bluetooth is turned on or off.
251     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
252     */
253    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
254    public static final String ACTION_REQUEST_ENABLE =
255            "android.bluetooth.adapter.action.REQUEST_ENABLE";
256
257    /**
258     * Activity Action: Show a system activity that allows user to enable BLE scans even when
259     * Bluetooth is turned off.<p>
260     *
261     * Notification of result of this activity is posted using
262     * {@link android.app.Activity#onActivityResult}. The <code>resultCode</code> will be
263     * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or
264     * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an
265     * error occurred.
266     *
267     * @hide
268     */
269    @SystemApi
270    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
271    public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE =
272            "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
273
274    /**
275     * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter
276     * has changed.
277     * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
278     * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes
279     * respectively.
280     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
281     */
282    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
283    public static final String ACTION_SCAN_MODE_CHANGED =
284            "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
285
286    /**
287     * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
288     * intents to request the current scan mode. Possible values are:
289     * {@link #SCAN_MODE_NONE},
290     * {@link #SCAN_MODE_CONNECTABLE},
291     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
292     */
293    public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE";
294    /**
295     * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
296     * intents to request the previous scan mode. Possible values are:
297     * {@link #SCAN_MODE_NONE},
298     * {@link #SCAN_MODE_CONNECTABLE},
299     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
300     */
301    public static final String EXTRA_PREVIOUS_SCAN_MODE =
302            "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE";
303
304    /** @hide */
305    @IntDef({SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE})
306    @Retention(RetentionPolicy.SOURCE)
307    public @interface ScanMode {}
308
309    /**
310     * Indicates that both inquiry scan and page scan are disabled on the local
311     * Bluetooth adapter. Therefore this device is neither discoverable
312     * nor connectable from remote Bluetooth devices.
313     */
314    public static final int SCAN_MODE_NONE = 20;
315    /**
316     * Indicates that inquiry scan is disabled, but page scan is enabled on the
317     * local Bluetooth adapter. Therefore this device is not discoverable from
318     * remote Bluetooth devices, but is connectable from remote devices that
319     * have previously discovered this device.
320     */
321    public static final int SCAN_MODE_CONNECTABLE = 21;
322    /**
323     * Indicates that both inquiry scan and page scan are enabled on the local
324     * Bluetooth adapter. Therefore this device is both discoverable and
325     * connectable from remote Bluetooth devices.
326     */
327    public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;
328
329    /**
330     * Broadcast Action: The local Bluetooth adapter has started the remote
331     * device discovery process.
332     * <p>This usually involves an inquiry scan of about 12 seconds, followed
333     * by a page scan of each new device to retrieve its Bluetooth name.
334     * <p>Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as
335     * remote Bluetooth devices are found.
336     * <p>Device discovery is a heavyweight procedure. New connections to
337     * remote Bluetooth devices should not be attempted while discovery is in
338     * progress, and existing connections will experience limited bandwidth
339     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
340     * discovery.
341     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
342     */
343    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
344    public static final String ACTION_DISCOVERY_STARTED =
345            "android.bluetooth.adapter.action.DISCOVERY_STARTED";
346    /**
347     * Broadcast Action: The local Bluetooth adapter has finished the device
348     * discovery process.
349     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
350     */
351    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
352    public static final String ACTION_DISCOVERY_FINISHED =
353            "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
354
355    /**
356     * Broadcast Action: The local Bluetooth adapter has changed its friendly
357     * Bluetooth name.
358     * <p>This name is visible to remote Bluetooth devices.
359     * <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing
360     * the name.
361     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
362     */
363    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
364    public static final String ACTION_LOCAL_NAME_CHANGED =
365            "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
366    /**
367     * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED}
368     * intents to request the local Bluetooth name.
369     */
370    public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME";
371
372    /**
373     * Intent used to broadcast the change in connection state of the local
374     * Bluetooth adapter to a profile of the remote device. When the adapter is
375     * not connected to any profiles of any remote devices and it attempts a
376     * connection to a profile this intent will sent. Once connected, this intent
377     * will not be sent for any more connection attempts to any profiles of any
378     * remote device. When the adapter disconnects from the last profile its
379     * connected to of any remote device, this intent will be sent.
380     *
381     * <p> This intent is useful for applications that are only concerned about
382     * whether the local adapter is connected to any profile of any device and
383     * are not really concerned about which profile. For example, an application
384     * which displays an icon to display whether Bluetooth is connected or not
385     * can use this intent.
386     *
387     * <p>This intent will have 3 extras:
388     * {@link #EXTRA_CONNECTION_STATE} - The current connection state.
389     * {@link #EXTRA_PREVIOUS_CONNECTION_STATE}- The previous connection state.
390     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
391     *
392     * {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE}
393     * can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
394     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
395     *
396     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
397     */
398    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
399    public static final String ACTION_CONNECTION_STATE_CHANGED =
400        "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
401
402    /**
403     * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
404     *
405     * This extra represents the current connection state.
406     */
407    public static final String EXTRA_CONNECTION_STATE =
408        "android.bluetooth.adapter.extra.CONNECTION_STATE";
409
410    /**
411     * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
412     *
413     * This extra represents the previous connection state.
414     */
415    public static final String EXTRA_PREVIOUS_CONNECTION_STATE =
416          "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE";
417
418    /**
419     * Broadcast Action: The Bluetooth adapter state has changed in LE only mode.
420     * @hide
421     */
422    @SystemApi
423    public static final String ACTION_BLE_STATE_CHANGED =
424        "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
425
426    /**
427     * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
428     * by BLE Always on enabled application to know the ACL_CONNECTED event
429     * when Bluetooth state in STATE_BLE_ON. This denotes GATT connection
430     * as Bluetooth LE is the only feature available in STATE_BLE_ON
431     *
432     * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which
433     * works in Bluetooth state STATE_ON
434     * @hide
435     */
436    public static final String ACTION_BLE_ACL_CONNECTED =
437        "android.bluetooth.adapter.action.BLE_ACL_CONNECTED";
438
439    /**
440     * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
441     * by BLE Always on enabled application to know the ACL_DISCONNECTED event
442     * when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth
443     * LE is the only feature available in STATE_BLE_ON
444     *
445     * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which
446     * works in Bluetooth state STATE_ON
447     * @hide
448     */
449    public static final String ACTION_BLE_ACL_DISCONNECTED =
450        "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
451
452    /** The profile is in disconnected state */
453    public static final int STATE_DISCONNECTED  = 0;
454    /** The profile is in connecting state */
455    public static final int STATE_CONNECTING    = 1;
456    /** The profile is in connected state */
457    public static final int STATE_CONNECTED     = 2;
458    /** The profile is in disconnecting state */
459    public static final int STATE_DISCONNECTING = 3;
460
461    /** @hide */
462    public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
463    private final IBinder mToken;
464
465
466    /** When creating a ServerSocket using listenUsingRfcommOn() or
467     *  listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create
468     *  a ServerSocket that auto assigns a channel number to the first
469     *  bluetooth socket.
470     *  The channel number assigned to this first Bluetooth Socket will
471     *  be stored in the ServerSocket, and reused for subsequent Bluetooth
472     *  sockets.
473     * @hide */
474    public static final int SOCKET_CHANNEL_AUTO_STATIC_NO_SDP = -2;
475
476
477    private static final int ADDRESS_LENGTH = 17;
478
479    /**
480     * Lazily initialized singleton. Guaranteed final after first object
481     * constructed.
482     */
483    private static BluetoothAdapter sAdapter;
484
485    private static BluetoothLeScanner sBluetoothLeScanner;
486    private static BluetoothLeAdvertiser sBluetoothLeAdvertiser;
487
488    private final IBluetoothManager mManagerService;
489    private IBluetooth mService;
490    private final ReentrantReadWriteLock mServiceLock =
491        new ReentrantReadWriteLock();
492
493    private final Object mLock = new Object();
494    private final Map<LeScanCallback, ScanCallback> mLeScanClients;
495
496    /**
497     * Get a handle to the default local Bluetooth adapter.
498     * <p>Currently Android only supports one Bluetooth adapter, but the API
499     * could be extended to support more. This will always return the default
500     * adapter.
501     * @return the default local adapter, or null if Bluetooth is not supported
502     *         on this hardware platform
503     */
504    public static synchronized BluetoothAdapter getDefaultAdapter() {
505        if (sAdapter == null) {
506            IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
507            if (b != null) {
508                IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
509                sAdapter = new BluetoothAdapter(managerService);
510            } else {
511                Log.e(TAG, "Bluetooth binder is null");
512            }
513        }
514        return sAdapter;
515    }
516
517    /**
518     * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
519     */
520    BluetoothAdapter(IBluetoothManager managerService) {
521
522        if (managerService == null) {
523            throw new IllegalArgumentException("bluetooth manager service is null");
524        }
525        try {
526            mServiceLock.writeLock().lock();
527            mService = managerService.registerAdapter(mManagerCallback);
528        } catch (RemoteException e) {
529            Log.e(TAG, "", e);
530        } finally {
531            mServiceLock.writeLock().unlock();
532        }
533        mManagerService = managerService;
534        mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
535        mToken = new Binder();
536    }
537
538    /**
539     * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
540     * address.
541     * <p>Valid Bluetooth hardware addresses must be upper case, in a format
542     * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is
543     * available to validate a Bluetooth address.
544     * <p>A {@link BluetoothDevice} will always be returned for a valid
545     * hardware address, even if this adapter has never seen that device.
546     *
547     * @param address valid Bluetooth MAC address
548     * @throws IllegalArgumentException if address is invalid
549     */
550    public BluetoothDevice getRemoteDevice(String address) {
551        return new BluetoothDevice(address);
552    }
553
554    /**
555     * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
556     * address.
557     * <p>Valid Bluetooth hardware addresses must be 6 bytes. This method
558     * expects the address in network byte order (MSB first).
559     * <p>A {@link BluetoothDevice} will always be returned for a valid
560     * hardware address, even if this adapter has never seen that device.
561     *
562     * @param address Bluetooth MAC address (6 bytes)
563     * @throws IllegalArgumentException if address is invalid
564     */
565    public BluetoothDevice getRemoteDevice(byte[] address) {
566        if (address == null || address.length != 6) {
567            throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
568        }
569        return new BluetoothDevice(String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
570                address[0], address[1], address[2], address[3], address[4], address[5]));
571    }
572
573    /**
574     * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations.
575     * Will return null if Bluetooth is turned off or if Bluetooth LE Advertising is not
576     * supported on this device.
577     * <p>
578     * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported
579     * on this device before calling this method.
580     */
581    public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
582        if (!getLeAccess()) return null;
583        if (!isMultipleAdvertisementSupported() && !isPeripheralModeSupported()) {
584            Log.e(TAG, "Bluetooth LE advertising not supported");
585            return null;
586        }
587        synchronized(mLock) {
588            if (sBluetoothLeAdvertiser == null) {
589                sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
590            }
591        }
592        return sBluetoothLeAdvertiser;
593    }
594
595    /**
596     * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
597     */
598    public BluetoothLeScanner getBluetoothLeScanner() {
599        if (!getLeAccess()) return null;
600        synchronized(mLock) {
601            if (sBluetoothLeScanner == null) {
602                sBluetoothLeScanner = new BluetoothLeScanner(mManagerService);
603            }
604        }
605        return sBluetoothLeScanner;
606    }
607
608    /**
609     * Return true if Bluetooth is currently enabled and ready for use.
610     * <p>Equivalent to:
611     * <code>getBluetoothState() == STATE_ON</code>
612     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
613     *
614     * @return true if the local adapter is turned on
615     */
616    @RequiresPermission(Manifest.permission.BLUETOOTH)
617    public boolean isEnabled() {
618        try {
619            mServiceLock.readLock().lock();
620            if (mService != null) return mService.isEnabled();
621        } catch (RemoteException e) {
622            Log.e(TAG, "", e);
623        } finally {
624            mServiceLock.readLock().unlock();
625        }
626
627        return false;
628    }
629
630    /**
631     * Return true if Bluetooth LE(Always BLE On feature) is currently
632     * enabled and ready for use
633     * <p>This returns true if current state is either STATE_ON or STATE_BLE_ON
634     *
635     * @return true if the local Bluetooth LE adapter is turned on
636     * @hide
637     */
638    @SystemApi
639    public boolean isLeEnabled() {
640       final int state = getLeState();
641       if (state == BluetoothAdapter.STATE_ON) {
642           if (DBG) Log.d (TAG, "STATE_ON");
643       } else if (state == BluetoothAdapter.STATE_BLE_ON) {
644           if (DBG) Log.d (TAG, "STATE_BLE_ON");
645       } else {
646           if (DBG) Log.d (TAG, "STATE_OFF");
647           return false;
648       }
649       return true;
650    }
651
652    /**
653     * Performs action based on user action to turn BT ON
654     * or OFF if BT is in BLE_ON state
655     */
656    private void notifyUserAction(boolean enable) {
657        try {
658            mServiceLock.readLock().lock();
659            if (mService == null) {
660                Log.e(TAG, "mService is null");
661                return;
662            }
663            if (enable) {
664                mService.onLeServiceUp(); //NA:TODO implementation pending
665            } else {
666                mService.onBrEdrDown(); //NA:TODO implementation pending
667            }
668        } catch (RemoteException e) {
669            Log.e(TAG, "", e);
670        } finally {
671            mServiceLock.readLock().unlock();
672        }
673    }
674
675    /**
676     * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE().
677     *
678     * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition
679     * to STATE_OFF and completely shut-down Bluetooth
680     *
681     * <p> If the Adapter state is STATE_ON, This would unregister the existance of
682     * special Bluetooth LE application and hence the further turning off of Bluetooth
683     * from UI would ensure the complete turn-off of Bluetooth rather than staying back
684     * BLE only state
685     *
686     * <p>This is an asynchronous call: it will return immediately, and
687     * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
688     * to be notified of subsequent adapter state changes If this call returns
689     * true, then the adapter state will immediately transition from {@link
690     * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
691     * later transition to either {@link #STATE_BLE_ON} or {@link
692     * #STATE_OFF} based on the existance of the further Always BLE ON enabled applications
693     * If this call returns false then there was an
694     * immediate problem that will prevent the QAdapter from being turned off -
695     * such as the QAadapter already being turned off.
696     *
697     * @return true to indicate success, or false on
698     *         immediate error
699     * @hide
700     */
701    @SystemApi
702    public boolean disableBLE() {
703        if (!isBleScanAlwaysAvailable()) return false;
704
705        int state = getLeState();
706        if (state == BluetoothAdapter.STATE_ON) {
707            if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable");
708            try {
709                mManagerService.updateBleAppCount(mToken, false);
710            } catch (RemoteException e) {
711                Log.e(TAG, "", e);
712            }
713            return true;
714
715        } else if (state == BluetoothAdapter.STATE_BLE_ON) {
716            if (DBG) Log.d (TAG, "STATE_BLE_ON");
717            int bleAppCnt = 0;
718            try {
719                bleAppCnt = mManagerService.updateBleAppCount(mToken, false);
720            } catch (RemoteException e) {
721                Log.e(TAG, "", e);
722            }
723            if (bleAppCnt == 0) {
724                // Disable only if there are no other clients
725                notifyUserAction(false);
726            }
727            return true;
728        }
729
730        if (DBG) Log.d (TAG, "STATE_OFF: Already disabled");
731        return false;
732    }
733
734    /**
735     * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would
736     * EnableBLE, EnableBLE brings-up Bluetooth so that application can access
737     * only LE related feature (Bluetooth GATT layers interfaces using the respective class)
738     * EnableBLE in turn registers the existance of a special App which wants to
739     * turn on Bluetooth Low enrgy part without making it visible at the settings UI
740     * as Bluetooth ON.
741     * <p>Invoking EnableBLE when Bluetooth is already in ON state, would just registers
742     * the existance of special Application and doesn't do anything to current BT state.
743     * when user turn OFF Bluetooth from UI, if there is an existance of special app, Bluetooth
744     * would stay in BLE_ON state so that LE features are still acessible to the special
745     * Applications.
746     *
747     * <p>This is an asynchronous call: it will return immediately, and
748     * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
749     * to be notified of subsequent adapter state changes. If this call returns
750     * true, then the adapter state will immediately transition from {@link
751     * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time
752     * later transition to either {@link #STATE_OFF} or {@link
753     * #STATE_BLE_ON}. If this call returns false then there was an
754     * immediate problem that will prevent the adapter from being turned on -
755     * such as Airplane mode, or the adapter is already turned on.
756     * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various
757     * states, It includes all the classic Bluetooth Adapter states along with
758     * internal BLE only states
759     *
760     * @return true to indicate Bluetooth LE start-up has begun, or false on
761     *         immediate error
762     * @hide
763     */
764    @SystemApi
765    public boolean enableBLE() {
766        if (!isBleScanAlwaysAvailable()) return false;
767
768        try {
769            mManagerService.updateBleAppCount(mToken, true);
770            if (isLeEnabled()) {
771                if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled");
772                return true;
773            }
774            if (DBG) Log.d(TAG, "enableBLE(): Calling enable");
775            return mManagerService.enable();
776        } catch (RemoteException e) {
777            Log.e(TAG, "", e);
778        }
779
780        return false;
781    }
782
783    /**
784     * Get the current state of the local Bluetooth adapter.
785     * <p>Possible return values are
786     * {@link #STATE_OFF},
787     * {@link #STATE_TURNING_ON},
788     * {@link #STATE_ON},
789     * {@link #STATE_TURNING_OFF}.
790     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
791     *
792     * @return current state of Bluetooth adapter
793     */
794    @RequiresPermission(Manifest.permission.BLUETOOTH)
795    @AdapterState
796    public int getState() {
797        int state = BluetoothAdapter.STATE_OFF;
798
799        try {
800            mServiceLock.readLock().lock();
801            if (mService != null) {
802                state = mService.getState();
803            }
804        } catch (RemoteException e) {
805            Log.e(TAG, "", e);
806        } finally {
807            mServiceLock.readLock().unlock();
808        }
809
810        // Consider all internal states as OFF
811        if (state == BluetoothAdapter.STATE_BLE_ON
812            || state == BluetoothAdapter.STATE_BLE_TURNING_ON
813            || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
814            if (VDBG) Log.d(TAG, "Consider internal state as OFF");
815            state = BluetoothAdapter.STATE_OFF;
816        }
817        if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
818        return state;
819    }
820
821    /**
822     * Get the current state of the local Bluetooth adapter
823     * <p>This returns current internal state of Adapter including LE ON/OFF
824     *
825     * <p>Possible return values are
826     * {@link #STATE_OFF},
827     * {@link #STATE_BLE_TURNING_ON},
828     * {@link #STATE_BLE_ON},
829     * {@link #STATE_TURNING_ON},
830     * {@link #STATE_ON},
831     * {@link #STATE_TURNING_OFF},
832     * {@link #STATE_BLE_TURNING_OFF}.
833     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
834     *
835     * @return current state of Bluetooth adapter
836     * @hide
837     */
838    @RequiresPermission(Manifest.permission.BLUETOOTH)
839    @AdapterState
840    public int getLeState() {
841        int state = BluetoothAdapter.STATE_OFF;
842
843        try {
844            mServiceLock.readLock().lock();
845            if (mService != null) {
846                state = mService.getState();
847            }
848        } catch (RemoteException e) {
849            Log.e(TAG, "", e);
850        } finally {
851            mServiceLock.readLock().unlock();
852        }
853
854        if (VDBG) Log.d(TAG,"getLeState() returning " + state);
855        return state;
856    }
857
858    boolean getLeAccess() {
859        if(getLeState() == STATE_ON)
860            return true;
861
862        else if (getLeState() == STATE_BLE_ON)
863            return true; // TODO: FILTER SYSTEM APPS HERE <--
864
865        return false;
866    }
867
868    /**
869     * Turn on the local Bluetooth adapter&mdash;do not use without explicit
870     * user action to turn on Bluetooth.
871     * <p>This powers on the underlying Bluetooth hardware, and starts all
872     * Bluetooth system services.
873     * <p class="caution"><strong>Bluetooth should never be enabled without
874     * direct user consent</strong>. If you want to turn on Bluetooth in order
875     * to create a wireless connection, you should use the {@link
876     * #ACTION_REQUEST_ENABLE} Intent, which will raise a dialog that requests
877     * user permission to turn on Bluetooth. The {@link #enable()} method is
878     * provided only for applications that include a user interface for changing
879     * system settings, such as a "power manager" app.</p>
880     * <p>This is an asynchronous call: it will return immediately, and
881     * clients should listen for {@link #ACTION_STATE_CHANGED}
882     * to be notified of subsequent adapter state changes. If this call returns
883     * true, then the adapter state will immediately transition from {@link
884     * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time
885     * later transition to either {@link #STATE_OFF} or {@link
886     * #STATE_ON}. If this call returns false then there was an
887     * immediate problem that will prevent the adapter from being turned on -
888     * such as Airplane mode, or the adapter is already turned on.
889     * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
890     * permission
891     *
892     * @return true to indicate adapter startup has begun, or false on
893     *         immediate error
894     */
895    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
896    public boolean enable() {
897        if (isEnabled() == true) {
898            if (DBG) Log.d(TAG, "enable(): BT is already enabled..!");
899            return true;
900        }
901        try {
902            return mManagerService.enable();
903        } catch (RemoteException e) {Log.e(TAG, "", e);}
904        return false;
905    }
906
907    /**
908     * Turn off the local Bluetooth adapter&mdash;do not use without explicit
909     * user action to turn off Bluetooth.
910     * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
911     * system services, and powers down the underlying Bluetooth hardware.
912     * <p class="caution"><strong>Bluetooth should never be disabled without
913     * direct user consent</strong>. The {@link #disable()} method is
914     * provided only for applications that include a user interface for changing
915     * system settings, such as a "power manager" app.</p>
916     * <p>This is an asynchronous call: it will return immediately, and
917     * clients should listen for {@link #ACTION_STATE_CHANGED}
918     * to be notified of subsequent adapter state changes. If this call returns
919     * true, then the adapter state will immediately transition from {@link
920     * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
921     * later transition to either {@link #STATE_OFF} or {@link
922     * #STATE_ON}. If this call returns false then there was an
923     * immediate problem that will prevent the adapter from being turned off -
924     * such as the adapter already being turned off.
925     * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
926     * permission
927     *
928     * @return true to indicate adapter shutdown has begun, or false on
929     *         immediate error
930     */
931    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
932    public boolean disable() {
933        try {
934            return mManagerService.disable(true);
935        } catch (RemoteException e) {Log.e(TAG, "", e);}
936        return false;
937    }
938
939    /**
940     * Turn off the local Bluetooth adapter and don't persist the setting.
941     *
942     * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
943     * permission
944     *
945     * @return true to indicate adapter shutdown has begun, or false on
946     *         immediate error
947     * @hide
948     */
949    public boolean disable(boolean persist) {
950
951        try {
952            return mManagerService.disable(persist);
953        } catch (RemoteException e) {Log.e(TAG, "", e);}
954        return false;
955    }
956
957    /**
958     * Returns the hardware address of the local Bluetooth adapter.
959     * <p>For example, "00:11:22:AA:BB:CC".
960     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
961     *
962     * @return Bluetooth hardware address as string
963     */
964    @RequiresPermission(Manifest.permission.BLUETOOTH)
965    public String getAddress() {
966        try {
967            return mManagerService.getAddress();
968        } catch (RemoteException e) {Log.e(TAG, "", e);}
969        return null;
970    }
971
972    /**
973     * Get the friendly Bluetooth name of the local Bluetooth adapter.
974     * <p>This name is visible to remote Bluetooth devices.
975     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
976     *
977     * @return the Bluetooth name, or null on error
978     */
979    public String getName() {
980        try {
981            return mManagerService.getName();
982        } catch (RemoteException e) {Log.e(TAG, "", e);}
983        return null;
984    }
985
986    /**
987     * enable or disable Bluetooth HCI snoop log.
988     *
989     * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
990     * permission
991     *
992     * @return true to indicate configure HCI log successfully, or false on
993     *         immediate error
994     * @hide
995     */
996    public boolean configHciSnoopLog(boolean enable) {
997        try {
998            mServiceLock.readLock().lock();
999            if (mService != null) return mService.configHciSnoopLog(enable);
1000        } catch (RemoteException e) {
1001            Log.e(TAG, "", e);
1002        } finally {
1003            mServiceLock.readLock().unlock();
1004        }
1005        return false;
1006    }
1007
1008    /**
1009     * Factory reset bluetooth settings.
1010     *
1011     * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
1012     * permission
1013     *
1014     * @return true to indicate that the config file was successfully cleared
1015     *
1016     * @hide
1017     */
1018    public boolean factoryReset() {
1019        try {
1020            mServiceLock.readLock().lock();
1021            if (mService != null) {
1022                return mService.factoryReset();
1023            }
1024            SystemProperties.set("persist.bluetooth.factoryreset", "true");
1025        } catch (RemoteException e) {
1026            Log.e(TAG, "", e);
1027        } finally {
1028            mServiceLock.readLock().unlock();
1029        }
1030        return false;
1031    }
1032
1033    /**
1034     * Get the UUIDs supported by the local Bluetooth adapter.
1035     *
1036     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
1037     *
1038     * @return the UUIDs supported by the local Bluetooth Adapter.
1039     * @hide
1040     */
1041    public ParcelUuid[] getUuids() {
1042        if (getState() != STATE_ON) return null;
1043        try {
1044            mServiceLock.readLock().lock();
1045            if (mService != null) return mService.getUuids();
1046        } catch (RemoteException e) {
1047            Log.e(TAG, "", e);
1048        } finally {
1049            mServiceLock.readLock().unlock();
1050        }
1051        return null;
1052    }
1053
1054    /**
1055     * Set the friendly Bluetooth name of the local Bluetooth adapter.
1056     * <p>This name is visible to remote Bluetooth devices.
1057     * <p>Valid Bluetooth names are a maximum of 248 bytes using UTF-8
1058     * encoding, although many remote devices can only display the first
1059     * 40 characters, and some may be limited to just 20.
1060     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1061     * will return false. After turning on Bluetooth,
1062     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1063     * to get the updated value.
1064     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
1065     *
1066     * @param name a valid Bluetooth name
1067     * @return     true if the name was set, false otherwise
1068     */
1069    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
1070    public boolean setName(String name) {
1071        if (getState() != STATE_ON) return false;
1072        try {
1073            mServiceLock.readLock().lock();
1074            if (mService != null) return mService.setName(name);
1075        } catch (RemoteException e) {
1076            Log.e(TAG, "", e);
1077        } finally {
1078            mServiceLock.readLock().unlock();
1079        }
1080        return false;
1081    }
1082
1083    /**
1084     * Get the current Bluetooth scan mode of the local Bluetooth adapter.
1085     * <p>The Bluetooth scan mode determines if the local adapter is
1086     * connectable and/or discoverable from remote Bluetooth devices.
1087     * <p>Possible values are:
1088     * {@link #SCAN_MODE_NONE},
1089     * {@link #SCAN_MODE_CONNECTABLE},
1090     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
1091     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1092     * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth,
1093     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1094     * to get the updated value.
1095     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
1096     *
1097     * @return scan mode
1098     */
1099    @RequiresPermission(Manifest.permission.BLUETOOTH)
1100    @ScanMode
1101    public int getScanMode() {
1102        if (getState() != STATE_ON) return SCAN_MODE_NONE;
1103        try {
1104            mServiceLock.readLock().lock();
1105            if (mService != null) return mService.getScanMode();
1106        } catch (RemoteException e) {
1107            Log.e(TAG, "", e);
1108        } finally {
1109            mServiceLock.readLock().unlock();
1110        }
1111        return SCAN_MODE_NONE;
1112    }
1113
1114    /**
1115     * Set the Bluetooth scan mode of the local Bluetooth adapter.
1116     * <p>The Bluetooth scan mode determines if the local adapter is
1117     * connectable and/or discoverable from remote Bluetooth devices.
1118     * <p>For privacy reasons, discoverable mode is automatically turned off
1119     * after <code>duration</code> seconds. For example, 120 seconds should be
1120     * enough for a remote device to initiate and complete its discovery
1121     * process.
1122     * <p>Valid scan mode values are:
1123     * {@link #SCAN_MODE_NONE},
1124     * {@link #SCAN_MODE_CONNECTABLE},
1125     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
1126     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1127     * will return false. After turning on Bluetooth,
1128     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1129     * to get the updated value.
1130     * <p>Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
1131     * <p>Applications cannot set the scan mode. They should use
1132     * <code>startActivityForResult(
1133     * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
1134     * </code>instead.
1135     *
1136     * @param mode valid scan mode
1137     * @param duration time in seconds to apply scan mode, only used for
1138     *                 {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}
1139     * @return     true if the scan mode was set, false otherwise
1140     * @hide
1141     */
1142    public boolean setScanMode(@ScanMode int mode, int duration) {
1143        if (getState() != STATE_ON) return false;
1144        try {
1145            mServiceLock.readLock().lock();
1146            if (mService != null) return mService.setScanMode(mode, duration);
1147        } catch (RemoteException e) {
1148            Log.e(TAG, "", e);
1149        } finally {
1150            mServiceLock.readLock().unlock();
1151        }
1152        return false;
1153    }
1154
1155    /** @hide */
1156    public boolean setScanMode(int mode) {
1157        if (getState() != STATE_ON) return false;
1158        /* getDiscoverableTimeout() to use the latest from NV than use 0 */
1159        return setScanMode(mode, getDiscoverableTimeout());
1160    }
1161
1162    /** @hide */
1163    public int getDiscoverableTimeout() {
1164        if (getState() != STATE_ON) return -1;
1165        try {
1166            mServiceLock.readLock().lock();
1167            if (mService != null) return mService.getDiscoverableTimeout();
1168        } catch (RemoteException e) {
1169            Log.e(TAG, "", e);
1170        } finally {
1171            mServiceLock.readLock().unlock();
1172        }
1173        return -1;
1174    }
1175
1176    /** @hide */
1177    public void setDiscoverableTimeout(int timeout) {
1178        if (getState() != STATE_ON) return;
1179        try {
1180            mServiceLock.readLock().lock();
1181            if (mService != null) mService.setDiscoverableTimeout(timeout);
1182        } catch (RemoteException e) {
1183            Log.e(TAG, "", e);
1184        } finally {
1185            mServiceLock.readLock().unlock();
1186        }
1187    }
1188
1189    /**
1190     * Start the remote device discovery process.
1191     * <p>The discovery process usually involves an inquiry scan of about 12
1192     * seconds, followed by a page scan of each new device to retrieve its
1193     * Bluetooth name.
1194     * <p>This is an asynchronous call, it will return immediately. Register
1195     * for {@link #ACTION_DISCOVERY_STARTED} and {@link
1196     * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the
1197     * discovery starts and completes. Register for {@link
1198     * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices
1199     * are found.
1200     * <p>Device discovery is a heavyweight procedure. New connections to
1201     * remote Bluetooth devices should not be attempted while discovery is in
1202     * progress, and existing connections will experience limited bandwidth
1203     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
1204     * discovery. Discovery is not managed by the Activity,
1205     * but is run as a system service, so an application should always call
1206     * {@link BluetoothAdapter#cancelDiscovery()} even if it
1207     * did not directly request a discovery, just to be sure.
1208     * <p>Device discovery will only find remote devices that are currently
1209     * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
1210     * not discoverable by default, and need to be entered into a special mode.
1211     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1212     * will return false. After turning on Bluetooth,
1213     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1214     * to get the updated value.
1215     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
1216     *
1217     * @return true on success, false on error
1218     */
1219    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
1220    public boolean startDiscovery() {
1221        if (getState() != STATE_ON) return false;
1222        try {
1223            mServiceLock.readLock().lock();
1224            if (mService != null) return mService.startDiscovery();
1225        } catch (RemoteException e) {
1226            Log.e(TAG, "", e);
1227        } finally {
1228            mServiceLock.readLock().unlock();
1229        }
1230        return false;
1231    }
1232
1233    /**
1234     * Cancel the current device discovery process.
1235     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
1236     * <p>Because discovery is a heavyweight procedure for the Bluetooth
1237     * adapter, this method should always be called before attempting to connect
1238     * to a remote device with {@link
1239     * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by
1240     * the  Activity, but is run as a system service, so an application should
1241     * always call cancel discovery even if it did not directly request a
1242     * discovery, just to be sure.
1243     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1244     * will return false. After turning on Bluetooth,
1245     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1246     * to get the updated value.
1247     *
1248     * @return true on success, false on error
1249     */
1250    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
1251    public boolean cancelDiscovery() {
1252        if (getState() != STATE_ON) return false;
1253        try {
1254            mServiceLock.readLock().lock();
1255            if (mService != null) return mService.cancelDiscovery();
1256        } catch (RemoteException e) {
1257            Log.e(TAG, "", e);
1258        } finally {
1259            mServiceLock.readLock().unlock();
1260        }
1261        return false;
1262    }
1263
1264    /**
1265     * Return true if the local Bluetooth adapter is currently in the device
1266     * discovery process.
1267     * <p>Device discovery is a heavyweight procedure. New connections to
1268     * remote Bluetooth devices should not be attempted while discovery is in
1269     * progress, and existing connections will experience limited bandwidth
1270     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
1271     * discovery.
1272     * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
1273     * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
1274     * starts or completes.
1275     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1276     * will return false. After turning on Bluetooth,
1277     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1278     * to get the updated value.
1279     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
1280     *
1281     * @return true if discovering
1282     */
1283    @RequiresPermission(Manifest.permission.BLUETOOTH)
1284    public boolean isDiscovering() {
1285        if (getState() != STATE_ON) return false;
1286        try {
1287            mServiceLock.readLock().lock();
1288            if (mService != null) return mService.isDiscovering();
1289        } catch (RemoteException e) {
1290            Log.e(TAG, "", e);
1291        } finally {
1292            mServiceLock.readLock().unlock();
1293        }
1294        return false;
1295    }
1296
1297    /**
1298     * Return true if the multi advertisement is supported by the chipset
1299     *
1300     * @return true if Multiple Advertisement feature is supported
1301     */
1302    public boolean isMultipleAdvertisementSupported() {
1303        if (getState() != STATE_ON) return false;
1304        try {
1305            mServiceLock.readLock().lock();
1306            if (mService != null) return mService.isMultiAdvertisementSupported();
1307        } catch (RemoteException e) {
1308            Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e);
1309        } finally {
1310            mServiceLock.readLock().unlock();
1311        }
1312        return false;
1313    }
1314
1315    /**
1316     * Returns {@code true} if BLE scan is always available, {@code false} otherwise. <p>
1317     *
1318     * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and
1319     * fetch scan results even when Bluetooth is turned off.<p>
1320     *
1321     * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}.
1322     *
1323     * @hide
1324     */
1325    @SystemApi
1326    public boolean isBleScanAlwaysAvailable() {
1327        try {
1328            return mManagerService.isBleScanAlwaysAvailable();
1329        } catch (RemoteException e) {
1330            Log.e(TAG, "remote expection when calling isBleScanAlwaysAvailable", e);
1331            return false;
1332        }
1333    }
1334
1335    /**
1336     * Returns whether peripheral mode is supported.
1337     *
1338     * @hide
1339     */
1340    public boolean isPeripheralModeSupported() {
1341        if (getState() != STATE_ON) return false;
1342        try {
1343            mServiceLock.readLock().lock();
1344            if (mService != null) return mService.isPeripheralModeSupported();
1345        } catch (RemoteException e) {
1346            Log.e(TAG, "failed to get peripheral mode capability: ", e);
1347        } finally {
1348            mServiceLock.readLock().unlock();
1349        }
1350        return false;
1351    }
1352
1353    /**
1354     * Return true if offloaded filters are supported
1355     *
1356     * @return true if chipset supports on-chip filtering
1357     */
1358    public boolean isOffloadedFilteringSupported() {
1359        if (!getLeAccess()) return false;
1360        try {
1361            mServiceLock.readLock().lock();
1362            if (mService != null) return mService.isOffloadedFilteringSupported();
1363        } catch (RemoteException e) {
1364            Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
1365        } finally {
1366            mServiceLock.readLock().unlock();
1367        }
1368        return false;
1369    }
1370
1371    /**
1372     * Return true if offloaded scan batching is supported
1373     *
1374     * @return true if chipset supports on-chip scan batching
1375     */
1376    public boolean isOffloadedScanBatchingSupported() {
1377        if (!getLeAccess()) return false;
1378        try {
1379            mServiceLock.readLock().lock();
1380            if (mService != null) return mService.isOffloadedScanBatchingSupported();
1381        } catch (RemoteException e) {
1382            Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e);
1383        } finally {
1384            mServiceLock.readLock().unlock();
1385        }
1386        return false;
1387    }
1388
1389    /**
1390     * Return true if hardware has entries available for matching beacons
1391     *
1392     * @return true if there are hw entries available for matching beacons
1393     * @hide
1394     */
1395    public boolean isHardwareTrackingFiltersAvailable() {
1396        if (!getLeAccess()) return false;
1397        try {
1398            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
1399            if (iGatt == null) {
1400                // BLE is not supported
1401                return false;
1402            }
1403            return (iGatt.numHwTrackFiltersAvailable() != 0);
1404        } catch (RemoteException e) {
1405            Log.e(TAG, "", e);
1406        }
1407        return false;
1408    }
1409
1410    /**
1411     * Return the record of {@link BluetoothActivityEnergyInfo} object that
1412     * has the activity and energy info. This can be used to ascertain what
1413     * the controller has been up to, since the last sample.
1414     * @param updateType Type of info, cached vs refreshed.
1415     *
1416     * @return a record with {@link BluetoothActivityEnergyInfo} or null if
1417     * report is unavailable or unsupported
1418     * @deprecated use the asynchronous
1419     * {@link #requestControllerActivityEnergyInfo(ResultReceiver)} instead.
1420     * @hide
1421     */
1422    @Deprecated
1423    public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
1424        SynchronousResultReceiver receiver = new SynchronousResultReceiver();
1425        requestControllerActivityEnergyInfo(receiver);
1426        try {
1427            SynchronousResultReceiver.Result result = receiver.awaitResult(1000);
1428            if (result.bundle != null) {
1429                return result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
1430            }
1431        } catch (TimeoutException e) {
1432            Log.e(TAG, "getControllerActivityEnergyInfo timed out");
1433        }
1434        return null;
1435    }
1436
1437    /**
1438     * Request the record of {@link BluetoothActivityEnergyInfo} object that
1439     * has the activity and energy info. This can be used to ascertain what
1440     * the controller has been up to, since the last sample.
1441     *
1442     * A null value for the activity info object may be sent if the bluetooth service is
1443     * unreachable or the device does not support reporting such information.
1444     *
1445     * @param result The callback to which to send the activity info.
1446     * @hide
1447     */
1448    public void requestControllerActivityEnergyInfo(ResultReceiver result) {
1449        try {
1450            mServiceLock.readLock().lock();
1451            if (mService != null) {
1452                mService.requestActivityInfo(result);
1453                result = null;
1454            }
1455        } catch (RemoteException e) {
1456            Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e);
1457        } finally {
1458            mServiceLock.readLock().unlock();
1459            if (result != null) {
1460                // Only send an immediate result if we failed.
1461                result.send(0, null);
1462            }
1463        }
1464    }
1465
1466    /**
1467     * Return the set of {@link BluetoothDevice} objects that are bonded
1468     * (paired) to the local adapter.
1469     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1470     * will return an empty set. After turning on Bluetooth,
1471     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1472     * to get the updated value.
1473     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
1474     *
1475     * @return unmodifiable set of {@link BluetoothDevice}, or null on error
1476     */
1477    @RequiresPermission(Manifest.permission.BLUETOOTH)
1478    public Set<BluetoothDevice> getBondedDevices() {
1479        if (getState() != STATE_ON) {
1480            return toDeviceSet(new BluetoothDevice[0]);
1481        }
1482        try {
1483            mServiceLock.readLock().lock();
1484            if (mService != null) return toDeviceSet(mService.getBondedDevices());
1485            return toDeviceSet(new BluetoothDevice[0]);
1486        } catch (RemoteException e) {
1487            Log.e(TAG, "", e);
1488        } finally {
1489            mServiceLock.readLock().unlock();
1490        }
1491        return null;
1492    }
1493
1494    /**
1495     * Get the current connection state of the local Bluetooth adapter.
1496     * This can be used to check whether the local Bluetooth adapter is connected
1497     * to any profile of any other remote Bluetooth Device.
1498     *
1499     * <p> Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED}
1500     * intent to get the connection state of the adapter.
1501     *
1502     * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED},
1503     * {@link #STATE_CONNECTING} or {@link #STATE_DISCONNECTED}
1504     *
1505     * @hide
1506     */
1507    public int getConnectionState() {
1508        if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED;
1509        try {
1510            mServiceLock.readLock().lock();
1511            if (mService != null) return mService.getAdapterConnectionState();
1512        } catch (RemoteException e) {
1513            Log.e(TAG, "getConnectionState:", e);
1514        } finally {
1515            mServiceLock.readLock().unlock();
1516        }
1517        return BluetoothAdapter.STATE_DISCONNECTED;
1518    }
1519
1520    /**
1521     * Get the current connection state of a profile.
1522     * This function can be used to check whether the local Bluetooth adapter
1523     * is connected to any remote device for a specific profile.
1524     * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
1525     * {@link BluetoothProfile#A2DP}.
1526     *
1527     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
1528     *
1529     * <p> Return value can be one of
1530     * {@link BluetoothProfile#STATE_DISCONNECTED},
1531     * {@link BluetoothProfile#STATE_CONNECTING},
1532     * {@link BluetoothProfile#STATE_CONNECTED},
1533     * {@link BluetoothProfile#STATE_DISCONNECTING}
1534     */
1535    @RequiresPermission(Manifest.permission.BLUETOOTH)
1536    public int getProfileConnectionState(int profile) {
1537        if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED;
1538        try {
1539            mServiceLock.readLock().lock();
1540            if (mService != null) return mService.getProfileConnectionState(profile);
1541        } catch (RemoteException e) {
1542            Log.e(TAG, "getProfileConnectionState:", e);
1543        } finally {
1544            mServiceLock.readLock().unlock();
1545        }
1546        return BluetoothProfile.STATE_DISCONNECTED;
1547    }
1548
1549    /**
1550     * Create a listening, secure RFCOMM Bluetooth socket.
1551     * <p>A remote device connecting to this socket will be authenticated and
1552     * communication on this socket will be encrypted.
1553     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
1554     * connections from a listening {@link BluetoothServerSocket}.
1555     * <p>Valid RFCOMM channels are in range 1 to 30.
1556     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
1557     * @param channel RFCOMM channel to listen on
1558     * @return a listening RFCOMM BluetoothServerSocket
1559     * @throws IOException on error, for example Bluetooth not available, or
1560     *                     insufficient permissions, or channel in use.
1561     * @hide
1562     */
1563    public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
1564        return listenUsingRfcommOn(channel, false, false);
1565    }
1566
1567    /**
1568     * Create a listening, secure RFCOMM Bluetooth socket.
1569     * <p>A remote device connecting to this socket will be authenticated and
1570     * communication on this socket will be encrypted.
1571     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
1572     * connections from a listening {@link BluetoothServerSocket}.
1573     * <p>Valid RFCOMM channels are in range 1 to 30.
1574     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
1575     * <p>To auto assign a channel without creating a SDP record use
1576     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
1577     * @param channel RFCOMM channel to listen on
1578     * @param mitm    enforce man-in-the-middle protection for authentication.
1579     * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 connections.
1580     * @return a listening RFCOMM BluetoothServerSocket
1581     * @throws IOException on error, for example Bluetooth not available, or
1582     *                     insufficient permissions, or channel in use.
1583     * @hide
1584     */
1585    public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm,
1586            boolean min16DigitPin)
1587            throws IOException {
1588        BluetoothServerSocket socket = new BluetoothServerSocket(
1589                BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm, min16DigitPin);
1590        int errno = socket.mSocket.bindListen();
1591        if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
1592            socket.setChannel(socket.mSocket.getPort());
1593        }
1594        if (errno != 0) {
1595            //TODO(BT): Throw the same exception error code
1596            // that the previous code was using.
1597            //socket.mSocket.throwErrnoNative(errno);
1598            throw new IOException("Error: " + errno);
1599        }
1600        return socket;
1601    }
1602
1603    /**
1604     * Create a listening, secure RFCOMM Bluetooth socket with Service Record.
1605     * <p>A remote device connecting to this socket will be authenticated and
1606     * communication on this socket will be encrypted.
1607     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
1608     * connections from a listening {@link BluetoothServerSocket}.
1609     * <p>The system will assign an unused RFCOMM channel to listen on.
1610     * <p>The system will also register a Service Discovery
1611     * Protocol (SDP) record with the local SDP server containing the specified
1612     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
1613     * can use the same UUID to query our SDP server and discover which channel
1614     * to connect to. This SDP record will be removed when this socket is
1615     * closed, or if this application closes unexpectedly.
1616     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
1617     * connect to this socket from another device using the same {@link UUID}.
1618     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
1619     * @param name service name for SDP record
1620     * @param uuid uuid for SDP record
1621     * @return a listening RFCOMM BluetoothServerSocket
1622     * @throws IOException on error, for example Bluetooth not available, or
1623     *                     insufficient permissions, or channel in use.
1624     */
1625    @RequiresPermission(Manifest.permission.BLUETOOTH)
1626    public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
1627            throws IOException {
1628        return createNewRfcommSocketAndRecord(name, uuid, true, true);
1629    }
1630
1631    /**
1632     * Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
1633     * <p>The link key is not required to be authenticated, i.e the communication may be
1634     * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices,
1635     * the link will be encrypted, as encryption is mandartory.
1636     * For legacy devices (pre Bluetooth 2.1 devices) the link will not
1637     * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an
1638     * encrypted and authenticated communication channel is desired.
1639     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
1640     * connections from a listening {@link BluetoothServerSocket}.
1641     * <p>The system will assign an unused RFCOMM channel to listen on.
1642     * <p>The system will also register a Service Discovery
1643     * Protocol (SDP) record with the local SDP server containing the specified
1644     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
1645     * can use the same UUID to query our SDP server and discover which channel
1646     * to connect to. This SDP record will be removed when this socket is
1647     * closed, or if this application closes unexpectedly.
1648     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
1649     * connect to this socket from another device using the same {@link UUID}.
1650     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
1651     * @param name service name for SDP record
1652     * @param uuid uuid for SDP record
1653     * @return a listening RFCOMM BluetoothServerSocket
1654     * @throws IOException on error, for example Bluetooth not available, or
1655     *                     insufficient permissions, or channel in use.
1656     */
1657    @RequiresPermission(Manifest.permission.BLUETOOTH)
1658    public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
1659            throws IOException {
1660        return createNewRfcommSocketAndRecord(name, uuid, false, false);
1661    }
1662
1663     /**
1664     * Create a listening, encrypted,
1665     * RFCOMM Bluetooth socket with Service Record.
1666     * <p>The link will be encrypted, but the link key is not required to be authenticated
1667     * i.e the communication is vulnerable to Man In the Middle attacks. Use
1668     * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key.
1669     * <p> Use this socket if authentication of link key is not possible.
1670     * For example, for Bluetooth 2.1 devices, if any of the devices does not have
1671     * an input and output capability or just has the ability to display a numeric key,
1672     * a secure socket connection is not possible and this socket can be used.
1673     * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required.
1674     * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandartory.
1675     * For more details, refer to the Security Model section 5.2 (vol 3) of
1676     * Bluetooth Core Specification version 2.1 + EDR.
1677     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
1678     * connections from a listening {@link BluetoothServerSocket}.
1679     * <p>The system will assign an unused RFCOMM channel to listen on.
1680     * <p>The system will also register a Service Discovery
1681     * Protocol (SDP) record with the local SDP server containing the specified
1682     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
1683     * can use the same UUID to query our SDP server and discover which channel
1684     * to connect to. This SDP record will be removed when this socket is
1685     * closed, or if this application closes unexpectedly.
1686     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
1687     * connect to this socket from another device using the same {@link UUID}.
1688     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
1689     * @param name service name for SDP record
1690     * @param uuid uuid for SDP record
1691     * @return a listening RFCOMM BluetoothServerSocket
1692     * @throws IOException on error, for example Bluetooth not available, or
1693     *                     insufficient permissions, or channel in use.
1694     * @hide
1695     */
1696    public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(
1697            String name, UUID uuid) throws IOException {
1698        return createNewRfcommSocketAndRecord(name, uuid, false, true);
1699    }
1700
1701
1702    private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
1703            boolean auth, boolean encrypt) throws IOException {
1704        BluetoothServerSocket socket;
1705        socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth,
1706                        encrypt, new ParcelUuid(uuid));
1707        socket.setServiceName(name);
1708        int errno = socket.mSocket.bindListen();
1709        if (errno != 0) {
1710            //TODO(BT): Throw the same exception error code
1711            // that the previous code was using.
1712            //socket.mSocket.throwErrnoNative(errno);
1713            throw new IOException("Error: " + errno);
1714        }
1715        return socket;
1716    }
1717
1718    /**
1719     * Construct an unencrypted, unauthenticated, RFCOMM server socket.
1720     * Call #accept to retrieve connections to this socket.
1721     * @return An RFCOMM BluetoothServerSocket
1722     * @throws IOException On error, for example Bluetooth not available, or
1723     *                     insufficient permissions.
1724     * @hide
1725     */
1726    public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
1727        BluetoothServerSocket socket = new BluetoothServerSocket(
1728                BluetoothSocket.TYPE_RFCOMM, false, false, port);
1729        int errno = socket.mSocket.bindListen();
1730        if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
1731            socket.setChannel(socket.mSocket.getPort());
1732        }
1733        if (errno != 0) {
1734            //TODO(BT): Throw the same exception error code
1735            // that the previous code was using.
1736            //socket.mSocket.throwErrnoNative(errno);
1737            throw new IOException("Error: " + errno);
1738        }
1739        return socket;
1740    }
1741
1742     /**
1743     * Construct an encrypted, RFCOMM server socket.
1744     * Call #accept to retrieve connections to this socket.
1745     * @return An RFCOMM BluetoothServerSocket
1746     * @throws IOException On error, for example Bluetooth not available, or
1747     *                     insufficient permissions.
1748     * @hide
1749     */
1750    public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port)
1751            throws IOException {
1752        BluetoothServerSocket socket = new BluetoothServerSocket(
1753                BluetoothSocket.TYPE_RFCOMM, false, true, port);
1754        int errno = socket.mSocket.bindListen();
1755        if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
1756            socket.setChannel(socket.mSocket.getPort());
1757        }
1758        if (errno < 0) {
1759            //TODO(BT): Throw the same exception error code
1760            // that the previous code was using.
1761            //socket.mSocket.throwErrnoNative(errno);
1762            throw new IOException("Error: " + errno);
1763        }
1764        return socket;
1765    }
1766
1767    /**
1768     * Construct a SCO server socket.
1769     * Call #accept to retrieve connections to this socket.
1770     * @return A SCO BluetoothServerSocket
1771     * @throws IOException On error, for example Bluetooth not available, or
1772     *                     insufficient permissions.
1773     * @hide
1774     */
1775    public static BluetoothServerSocket listenUsingScoOn() throws IOException {
1776        BluetoothServerSocket socket = new BluetoothServerSocket(
1777                BluetoothSocket.TYPE_SCO, false, false, -1);
1778        int errno = socket.mSocket.bindListen();
1779        if (errno < 0) {
1780            //TODO(BT): Throw the same exception error code
1781            // that the previous code was using.
1782            //socket.mSocket.throwErrnoNative(errno);
1783        }
1784        return socket;
1785    }
1786
1787    /**
1788     * Construct an encrypted, authenticated, L2CAP server socket.
1789     * Call #accept to retrieve connections to this socket.
1790     * <p>To auto assign a port without creating a SDP record use
1791     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
1792     * @param port    the PSM to listen on
1793     * @param mitm    enforce man-in-the-middle protection for authentication.
1794     * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 connections.
1795     * @return An L2CAP BluetoothServerSocket
1796     * @throws IOException On error, for example Bluetooth not available, or
1797     *                     insufficient permissions.
1798     * @hide
1799     */
1800    public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin)
1801            throws IOException {
1802        BluetoothServerSocket socket = new BluetoothServerSocket(
1803                BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, min16DigitPin);
1804        int errno = socket.mSocket.bindListen();
1805        if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
1806            socket.setChannel(socket.mSocket.getPort());
1807        }
1808        if (errno != 0) {
1809            //TODO(BT): Throw the same exception error code
1810            // that the previous code was using.
1811            //socket.mSocket.throwErrnoNative(errno);
1812            throw new IOException("Error: " + errno);
1813        }
1814        return socket;
1815    }
1816
1817    /**
1818     * Construct an encrypted, authenticated, L2CAP server socket.
1819     * Call #accept to retrieve connections to this socket.
1820     * <p>To auto assign a port without creating a SDP record use
1821     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
1822     * @param port    the PSM to listen on
1823     * @return An L2CAP BluetoothServerSocket
1824     * @throws IOException On error, for example Bluetooth not available, or
1825     *                     insufficient permissions.
1826     * @hide
1827     */
1828    public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException {
1829        return listenUsingL2capOn(port, false, false);
1830    }
1831
1832    /**
1833     * Read the local Out of Band Pairing Data
1834     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
1835     *
1836     * @return Pair<byte[], byte[]> of Hash and Randomizer
1837     *
1838     * @hide
1839     */
1840    public Pair<byte[], byte[]> readOutOfBandData() {
1841        if (getState() != STATE_ON) return null;
1842        //TODO(BT
1843        /*
1844        try {
1845            byte[] hash;
1846            byte[] randomizer;
1847
1848            byte[] ret = null;
1849            mServiceLock.readLock().lock();
1850            if (mService != null) mService.readOutOfBandData();
1851
1852            if (ret  == null || ret.length != 32) return null;
1853
1854            hash = Arrays.copyOfRange(ret, 0, 16);
1855            randomizer = Arrays.copyOfRange(ret, 16, 32);
1856
1857            if (DBG) {
1858                Log.d(TAG, "readOutOfBandData:" + Arrays.toString(hash) +
1859                  ":" + Arrays.toString(randomizer));
1860            }
1861            return new Pair<byte[], byte[]>(hash, randomizer);
1862
1863        } catch (RemoteException e) {
1864            Log.e(TAG, "", e);
1865        } finally {
1866            mServiceLock.readLock().unlock();
1867        }
1868        */
1869        return null;
1870    }
1871
1872    /**
1873     * Get the profile proxy object associated with the profile.
1874     *
1875     * <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
1876     * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or
1877     * {@link BluetoothProfile#GATT_SERVER}. Clients must implement
1878     * {@link BluetoothProfile.ServiceListener} to get notified of
1879     * the connection status and to get the proxy object.
1880     *
1881     * @param context Context of the application
1882     * @param listener The service Listener for connection callbacks.
1883     * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH},
1884     *                {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
1885     *                {@link BluetoothProfile#GATT} or {@link BluetoothProfile#GATT_SERVER}.
1886     * @return true on success, false on error
1887     */
1888    public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
1889                                   int profile) {
1890        if (context == null || listener == null) return false;
1891
1892        if (profile == BluetoothProfile.HEADSET) {
1893            BluetoothHeadset headset = new BluetoothHeadset(context, listener);
1894            return true;
1895        } else if (profile == BluetoothProfile.A2DP) {
1896            BluetoothA2dp a2dp = new BluetoothA2dp(context, listener);
1897            return true;
1898        } else if (profile == BluetoothProfile.A2DP_SINK) {
1899            BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener);
1900            return true;
1901        } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
1902            BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener);
1903            return true;
1904        } else if (profile == BluetoothProfile.INPUT_DEVICE) {
1905            BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener);
1906            return true;
1907        } else if (profile == BluetoothProfile.PAN) {
1908            BluetoothPan pan = new BluetoothPan(context, listener);
1909            return true;
1910        } else if (profile == BluetoothProfile.HEALTH) {
1911            BluetoothHealth health = new BluetoothHealth(context, listener);
1912            return true;
1913        } else if (profile == BluetoothProfile.MAP) {
1914            BluetoothMap map = new BluetoothMap(context, listener);
1915            return true;
1916        } else if (profile == BluetoothProfile.HEADSET_CLIENT) {
1917            BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener);
1918            return true;
1919        } else if (profile == BluetoothProfile.SAP) {
1920            BluetoothSap sap = new BluetoothSap(context, listener);
1921            return true;
1922        } else if (profile == BluetoothProfile.PBAP_CLIENT) {
1923            BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener);
1924            return true;
1925        } else {
1926            return false;
1927        }
1928    }
1929
1930    /**
1931     * Close the connection of the profile proxy to the Service.
1932     *
1933     * <p> Clients should call this when they are no longer using
1934     * the proxy obtained from {@link #getProfileProxy}.
1935     * Profile can be one of  {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or
1936     * {@link BluetoothProfile#A2DP}
1937     *
1938     * @param profile
1939     * @param proxy Profile proxy object
1940     */
1941    public void closeProfileProxy(int profile, BluetoothProfile proxy) {
1942        if (proxy == null) return;
1943
1944        switch (profile) {
1945            case BluetoothProfile.HEADSET:
1946                BluetoothHeadset headset = (BluetoothHeadset)proxy;
1947                headset.close();
1948                break;
1949            case BluetoothProfile.A2DP:
1950                BluetoothA2dp a2dp = (BluetoothA2dp)proxy;
1951                a2dp.close();
1952                break;
1953            case BluetoothProfile.A2DP_SINK:
1954                BluetoothA2dpSink a2dpSink = (BluetoothA2dpSink)proxy;
1955                a2dpSink.close();
1956                break;
1957            case BluetoothProfile.AVRCP_CONTROLLER:
1958                BluetoothAvrcpController avrcp = (BluetoothAvrcpController)proxy;
1959                avrcp.close();
1960                break;
1961            case BluetoothProfile.INPUT_DEVICE:
1962                BluetoothInputDevice iDev = (BluetoothInputDevice)proxy;
1963                iDev.close();
1964                break;
1965            case BluetoothProfile.PAN:
1966                BluetoothPan pan = (BluetoothPan)proxy;
1967                pan.close();
1968                break;
1969            case BluetoothProfile.HEALTH:
1970                BluetoothHealth health = (BluetoothHealth)proxy;
1971                health.close();
1972                break;
1973           case BluetoothProfile.GATT:
1974                BluetoothGatt gatt = (BluetoothGatt)proxy;
1975                gatt.close();
1976                break;
1977            case BluetoothProfile.GATT_SERVER:
1978                BluetoothGattServer gattServer = (BluetoothGattServer)proxy;
1979                gattServer.close();
1980                break;
1981            case BluetoothProfile.MAP:
1982                BluetoothMap map = (BluetoothMap)proxy;
1983                map.close();
1984                break;
1985            case BluetoothProfile.HEADSET_CLIENT:
1986                BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient)proxy;
1987                headsetClient.close();
1988                break;
1989            case BluetoothProfile.SAP:
1990                BluetoothSap sap = (BluetoothSap)proxy;
1991                sap.close();
1992                break;
1993            case BluetoothProfile.PBAP_CLIENT:
1994                BluetoothPbapClient pbapClient = (BluetoothPbapClient)proxy;
1995                pbapClient.close();
1996                break;
1997        }
1998    }
1999
2000    final private IBluetoothManagerCallback mManagerCallback =
2001        new IBluetoothManagerCallback.Stub() {
2002            public void onBluetoothServiceUp(IBluetooth bluetoothService) {
2003                if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
2004
2005                mServiceLock.writeLock().lock();
2006                mService = bluetoothService;
2007                mServiceLock.writeLock().unlock();
2008
2009                synchronized (mProxyServiceStateCallbacks) {
2010                    for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ) {
2011                        try {
2012                            if (cb != null) {
2013                                cb.onBluetoothServiceUp(bluetoothService);
2014                            } else {
2015                                Log.d(TAG, "onBluetoothServiceUp: cb is null!!!");
2016                            }
2017                        } catch (Exception e) {
2018                            Log.e(TAG,"",e);
2019                        }
2020                    }
2021                }
2022            }
2023
2024            public void onBluetoothServiceDown() {
2025                if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
2026
2027                try {
2028                    mServiceLock.writeLock().lock();
2029                    mService = null;
2030                    if (mLeScanClients != null) mLeScanClients.clear();
2031                    if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup();
2032                    if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup();
2033                } finally {
2034                    mServiceLock.writeLock().unlock();
2035                }
2036
2037                synchronized (mProxyServiceStateCallbacks) {
2038                    for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
2039                        try {
2040                            if (cb != null) {
2041                                cb.onBluetoothServiceDown();
2042                            } else {
2043                                Log.d(TAG, "onBluetoothServiceDown: cb is null!!!");
2044                            }
2045                        } catch (Exception e) {
2046                            Log.e(TAG,"",e);
2047                        }
2048                    }
2049                }
2050            }
2051
2052            public void onBrEdrDown() {
2053                if (DBG) Log.i(TAG, "onBrEdrDown:");
2054            }
2055    };
2056
2057    /**
2058     * Enable the Bluetooth Adapter, but don't auto-connect devices
2059     * and don't persist state. Only for use by system applications.
2060     * @hide
2061     */
2062    public boolean enableNoAutoConnect() {
2063        if (isEnabled() == true){
2064            if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT is already enabled..!");
2065            return true;
2066        }
2067        try {
2068            return mManagerService.enableNoAutoConnect();
2069        } catch (RemoteException e) {Log.e(TAG, "", e);}
2070        return false;
2071    }
2072
2073    /**
2074     * Enable control of the Bluetooth Adapter for a single application.
2075     *
2076     * <p>Some applications need to use Bluetooth for short periods of time to
2077     * transfer data but don't want all the associated implications like
2078     * automatic connection to headsets etc.
2079     *
2080     * <p> Multiple applications can call this. This is reference counted and
2081     * Bluetooth disabled only when no one else is using it. There will be no UI
2082     * shown to the user while bluetooth is being enabled. Any user action will
2083     * override this call. For example, if user wants Bluetooth on and the last
2084     * user of this API wanted to disable Bluetooth, Bluetooth will not be
2085     * turned off.
2086     *
2087     * <p> This API is only meant to be used by internal applications. Third
2088     * party applications but use {@link #enable} and {@link #disable} APIs.
2089     *
2090     * <p> If this API returns true, it means the callback will be called.
2091     * The callback will be called with the current state of Bluetooth.
2092     * If the state is not what was requested, an internal error would be the
2093     * reason. If Bluetooth is already on and if this function is called to turn
2094     * it on, the api will return true and a callback will be called.
2095     *
2096     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
2097     *
2098     * @param on True for on, false for off.
2099     * @param callback The callback to notify changes to the state.
2100     * @hide
2101     */
2102    public boolean changeApplicationBluetoothState(boolean on,
2103                                                   BluetoothStateChangeCallback callback) {
2104        if (callback == null) return false;
2105
2106        //TODO(BT)
2107        /*
2108        try {
2109            mServiceLock.readLock().lock();
2110            if (mService != null) {
2111                return mService.changeApplicationBluetoothState(on, new
2112                    StateChangeCallbackWrapper(callback), new Binder());
2113            }
2114        } catch (RemoteException e) {
2115            Log.e(TAG, "changeBluetoothState", e);
2116        } finally {
2117            mServiceLock.readLock().unlock();
2118        }
2119        */
2120        return false;
2121    }
2122
2123    /**
2124     * @hide
2125     */
2126    public interface BluetoothStateChangeCallback {
2127        public void onBluetoothStateChange(boolean on);
2128    }
2129
2130    /**
2131     * @hide
2132     */
2133    public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub {
2134        private BluetoothStateChangeCallback mCallback;
2135
2136        StateChangeCallbackWrapper(BluetoothStateChangeCallback
2137                callback) {
2138            mCallback = callback;
2139        }
2140
2141        @Override
2142        public void onBluetoothStateChange(boolean on) {
2143            mCallback.onBluetoothStateChange(on);
2144        }
2145    }
2146
2147    private Set<BluetoothDevice> toDeviceSet(BluetoothDevice[] devices) {
2148        Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(Arrays.asList(devices));
2149        return Collections.unmodifiableSet(deviceSet);
2150    }
2151
2152    protected void finalize() throws Throwable {
2153        try {
2154            mManagerService.unregisterAdapter(mManagerCallback);
2155        } catch (RemoteException e) {
2156            Log.e(TAG, "", e);
2157        } finally {
2158            super.finalize();
2159        }
2160    }
2161
2162
2163    /**
2164     * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0"
2165     * <p>Alphabetic characters must be uppercase to be valid.
2166     *
2167     * @param address Bluetooth address as string
2168     * @return true if the address is valid, false otherwise
2169     */
2170    public static boolean checkBluetoothAddress(String address) {
2171        if (address == null || address.length() != ADDRESS_LENGTH) {
2172            return false;
2173        }
2174        for (int i = 0; i < ADDRESS_LENGTH; i++) {
2175            char c = address.charAt(i);
2176            switch (i % 3) {
2177            case 0:
2178            case 1:
2179                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
2180                    // hex character, OK
2181                    break;
2182                }
2183                return false;
2184            case 2:
2185                if (c == ':') {
2186                    break;  // OK
2187                }
2188                return false;
2189            }
2190        }
2191        return true;
2192    }
2193
2194    /*package*/ IBluetoothManager getBluetoothManager() {
2195            return mManagerService;
2196    }
2197
2198    final private ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks = new ArrayList<IBluetoothManagerCallback>();
2199
2200    /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) {
2201        synchronized (mProxyServiceStateCallbacks) {
2202            if (cb == null) {
2203                Log.w(TAG, "getBluetoothService() called with no BluetoothManagerCallback");
2204            } else if (!mProxyServiceStateCallbacks.contains(cb)) {
2205                mProxyServiceStateCallbacks.add(cb);
2206            }
2207        }
2208        return mService;
2209    }
2210
2211    /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) {
2212        synchronized (mProxyServiceStateCallbacks) {
2213            mProxyServiceStateCallbacks.remove(cb);
2214        }
2215    }
2216
2217    /**
2218     * Callback interface used to deliver LE scan results.
2219     *
2220     * @see #startLeScan(LeScanCallback)
2221     * @see #startLeScan(UUID[], LeScanCallback)
2222     */
2223    public interface LeScanCallback {
2224        /**
2225         * Callback reporting an LE device found during a device scan initiated
2226         * by the {@link BluetoothAdapter#startLeScan} function.
2227         *
2228         * @param device Identifies the remote device
2229         * @param rssi The RSSI value for the remote device as reported by the
2230         *             Bluetooth hardware. 0 if no RSSI value is available.
2231         * @param scanRecord The content of the advertisement record offered by
2232         *                   the remote device.
2233         */
2234        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
2235    }
2236
2237    /**
2238     * Starts a scan for Bluetooth LE devices.
2239     *
2240     * <p>Results of the scan are reported using the
2241     * {@link LeScanCallback#onLeScan} callback.
2242     *
2243     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
2244     *
2245     * @param callback the callback LE scan results are delivered
2246     * @return true, if the scan was started successfully
2247     * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)}
2248     *             instead.
2249     */
2250    @Deprecated
2251    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
2252    public boolean startLeScan(LeScanCallback callback) {
2253        return startLeScan(null, callback);
2254    }
2255
2256    /**
2257     * Starts a scan for Bluetooth LE devices, looking for devices that
2258     * advertise given services.
2259     *
2260     * <p>Devices which advertise all specified services are reported using the
2261     * {@link LeScanCallback#onLeScan} callback.
2262     *
2263     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
2264     *
2265     * @param serviceUuids Array of services to look for
2266     * @param callback the callback LE scan results are delivered
2267     * @return true, if the scan was started successfully
2268     * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)}
2269     *             instead.
2270     */
2271    @Deprecated
2272    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
2273    public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
2274        if (DBG) Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
2275        if (callback == null) {
2276            if (DBG) Log.e(TAG, "startLeScan: null callback");
2277            return false;
2278        }
2279        BluetoothLeScanner scanner = getBluetoothLeScanner();
2280        if (scanner == null) {
2281            if (DBG) Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
2282            return false;
2283        }
2284
2285        synchronized(mLeScanClients) {
2286            if (mLeScanClients.containsKey(callback)) {
2287                if (DBG) Log.e(TAG, "LE Scan has already started");
2288                return false;
2289            }
2290
2291            try {
2292                IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
2293                if (iGatt == null) {
2294                    // BLE is not supported
2295                    return false;
2296                }
2297
2298                ScanCallback scanCallback = new ScanCallback() {
2299                    @Override
2300                    public void onScanResult(int callbackType, ScanResult result) {
2301                        if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
2302                            // Should not happen.
2303                            Log.e(TAG, "LE Scan has already started");
2304                            return;
2305                        }
2306                        ScanRecord scanRecord = result.getScanRecord();
2307                        if (scanRecord == null) {
2308                            return;
2309                        }
2310                        if (serviceUuids != null) {
2311                            List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
2312                            for (UUID uuid : serviceUuids) {
2313                                uuids.add(new ParcelUuid(uuid));
2314                            }
2315                            List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids();
2316                            if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) {
2317                                if (DBG) Log.d(TAG, "uuids does not match");
2318                                return;
2319                            }
2320                        }
2321                        callback.onLeScan(result.getDevice(), result.getRssi(),
2322                                scanRecord.getBytes());
2323                    }
2324                };
2325                ScanSettings settings = new ScanSettings.Builder()
2326                    .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
2327                    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
2328
2329                List<ScanFilter> filters = new ArrayList<ScanFilter>();
2330                if (serviceUuids != null && serviceUuids.length > 0) {
2331                    // Note scan filter does not support matching an UUID array so we put one
2332                    // UUID to hardware and match the whole array in callback.
2333                    ScanFilter filter = new ScanFilter.Builder().setServiceUuid(
2334                            new ParcelUuid(serviceUuids[0])).build();
2335                    filters.add(filter);
2336                }
2337                scanner.startScan(filters, settings, scanCallback);
2338
2339                mLeScanClients.put(callback, scanCallback);
2340                return true;
2341
2342            } catch (RemoteException e) {
2343                Log.e(TAG,"",e);
2344            }
2345        }
2346        return false;
2347    }
2348
2349    /**
2350     * Stops an ongoing Bluetooth LE device scan.
2351     *
2352     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
2353     *
2354     * @param callback used to identify which scan to stop
2355     *        must be the same handle used to start the scan
2356     * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead.
2357     */
2358    @Deprecated
2359    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
2360    public void stopLeScan(LeScanCallback callback) {
2361        if (DBG) Log.d(TAG, "stopLeScan()");
2362        BluetoothLeScanner scanner = getBluetoothLeScanner();
2363        if (scanner == null) {
2364            return;
2365        }
2366        synchronized (mLeScanClients) {
2367            ScanCallback scanCallback = mLeScanClients.remove(callback);
2368            if (scanCallback == null) {
2369                if (DBG) Log.d(TAG, "scan not started yet");
2370                return;
2371            }
2372            scanner.stopScan(scanCallback);
2373        }
2374    }
2375}
2376