Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a2796a7a authored by Wei Wang's avatar Wei Wang Committed by Android (Google) Code Review
Browse files

Merge "Use Scanner for legacy LE scans(1/2)." into lmp-dev

parents 1d97d398 c3059ccf
Loading
Loading
Loading
Loading
+91 −273
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
@@ -371,12 +373,14 @@ public final class BluetoothAdapter {
     */
    private static BluetoothAdapter sAdapter;

    private static BluetoothLeScanner sBluetoothLeScanner;
    private static BluetoothLeAdvertiser sBluetoothLeAdvertiser;

    private final IBluetoothManager mManagerService;
    private IBluetooth mService;

    private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;
    private final Handler mHandler;  // Handler to post the advertise callback to run on main thread.
    private final Object mLock = new Object();
    private final Map<LeScanCallback, ScanCallback> mLeScanClients;

    /**
     * Get a handle to the default local Bluetooth adapter.
@@ -411,8 +415,7 @@ public final class BluetoothAdapter {
            mService = managerService.registerAdapter(mManagerCallback);
        } catch (RemoteException e) {Log.e(TAG, "", e);}
        mManagerService = managerService;
        mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>();
        mHandler = new Handler(Looper.getMainLooper());
        mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
    }

    /**
@@ -451,19 +454,40 @@ public final class BluetoothAdapter {
    }

    /**
     * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations.
     * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations, or
     * null if Bluetooth LE Advertising is not support on this device.
     * <p>
     * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported
     * on this device before calling this method.
     */
    public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
        // TODO: Return null if this feature is not supported by hardware.
        return new BluetoothLeAdvertiser(mManagerService);
        if (getState() != STATE_ON) {
            return null;
        }
        if (!isMultipleAdvertisementSupported()) {
            return null;
        }
        synchronized(mLock) {
            if (sBluetoothLeAdvertiser == null) {
                sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
            }
        }
        return sBluetoothLeAdvertiser;
    }

    /**
     * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
     */
    public BluetoothLeScanner getBluetoothLeScanner() {
        // TODO: Return null if BLE scan is not supported by hardware.
        return new BluetoothLeScanner(mManagerService);
        if (getState() != STATE_ON) {
            return null;
        }
        synchronized(mLock) {
            if (sBluetoothLeScanner == null) {
                sBluetoothLeScanner = new BluetoothLeScanner(mManagerService);
            }
        }
        return sBluetoothLeScanner;
    }

    /**
@@ -1625,13 +1649,17 @@ public final class BluetoothAdapter {
     *             instead.
     */
    @Deprecated
    public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) {
    public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
        if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids);

        if (callback == null) {
            if (DBG) Log.e(TAG, "startLeScan: null callback");
            return false;
        }
        BluetoothLeScanner scanner = getBluetoothLeScanner();
        if (scanner == null) {
            if (DBG) Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
            return false;
        }

        synchronized(mLeScanClients) {
            if (mLeScanClients.containsKey(callback)) {
@@ -1646,13 +1674,50 @@ public final class BluetoothAdapter {
                    return false;
                }

                UUID uuid = UUID.randomUUID();
                GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids);
                iGatt.registerClient(new ParcelUuid(uuid), wrapper);
                if (wrapper.scanStarted()) {
                    mLeScanClients.put(callback, wrapper);
                    return true;
                ScanCallback scanCallback = new ScanCallback() {
                    @Override
                    public void onScanResult(int callbackType, ScanResult result) {
                        if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
                            // Should not happen.
                            Log.e(TAG, "LE Scan has already started");
                            return;
                        }
                        ScanRecord scanRecord = result.getScanRecord();
                        if (scanRecord == null) {
                            return;
                        }
                        if (serviceUuids != null) {
                            List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
                            for (UUID uuid : serviceUuids) {
                                uuids.add(new ParcelUuid(uuid));
                            }
                            List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids();
                            if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) {
                                if (DBG) Log.d(TAG, "uuids does not match");
                                return;
                            }
                        }
                        callback.onLeScan(result.getDevice(), result.getRssi(),
                                scanRecord.getBytes());
                    }
                };
                ScanSettings settings = new ScanSettings.Builder()
                    .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
                    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();

                List<ScanFilter> filters = new ArrayList<ScanFilter>();
                if (serviceUuids != null && serviceUuids.length > 0) {
                    // Note scan filter does not support matching an UUID array so we put one
                    // UUID to hardware and match the whole array in callback.
                    ScanFilter filter = new ScanFilter.Builder().setServiceUuid(
                            new ParcelUuid(serviceUuids[0])).build();
                    filters.add(filter);
                }
                scanner.startScan(filters, settings, scanCallback);

                mLeScanClients.put(callback, scanCallback);
                return true;

            } catch (RemoteException e) {
                Log.e(TAG,"",e);
            }
@@ -1672,264 +1737,17 @@ public final class BluetoothAdapter {
    @Deprecated
    public void stopLeScan(LeScanCallback callback) {
        if (DBG) Log.d(TAG, "stopLeScan()");
        GattCallbackWrapper wrapper;
        synchronized(mLeScanClients) {
            wrapper = mLeScanClients.remove(callback);
            if (wrapper == null) return;
        }
        wrapper.stopLeScan();
    }

    /**
     * Bluetooth GATT interface callbacks
     */
    private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub {
        private static final int LE_CALLBACK_REG_TIMEOUT = 2000;
        private static final int LE_CALLBACK_REG_WAIT_COUNT = 5;

        private final LeScanCallback mLeScanCb;

        // mLeHandle 0: not registered
        //           -1: scan stopped
        //           >0: registered and scan started
        private int mLeHandle;
        private final UUID[] mScanFilter;
        private WeakReference<BluetoothAdapter> mBluetoothAdapter;

        public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter,
                                   LeScanCallback leScanCb, UUID[] uuid) {
            mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter);
            mLeScanCb = leScanCb;
            mScanFilter = uuid;
            mLeHandle = 0;
        }

        public boolean scanStarted() {
            return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT);
        }

        private boolean waitForRegisteration(int maxWaitCount) {
            boolean started = false;
            synchronized(this) {
                if (mLeHandle == -1) return false;
                int count = 0;
                // wait for callback registration and LE scan to start
                while (mLeHandle == 0 && count < maxWaitCount) {
                    try {
                        wait(LE_CALLBACK_REG_TIMEOUT);
                    } catch (InterruptedException e) {
                        Log.e(TAG, "Callback reg wait interrupted: " + e);
                    }
                    count++;
                }
                started = (mLeHandle > 0);
            }
            return started;
        }

        public void stopLeScan() {
            synchronized(this) {
                if (mLeHandle <= 0) {
                    Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
        BluetoothLeScanner scanner = getBluetoothLeScanner();
        if (scanner == null) {
            return;
        }
                BluetoothAdapter adapter = mBluetoothAdapter.get();
                if (adapter != null) {
                    try {
                        IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt();
                        iGatt.stopScan(mLeHandle, false);
                        iGatt.unregisterClient(mLeHandle);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Failed to stop scan and unregister" + e);
                    }
                } else {
                    Log.e(TAG, "stopLeScan, BluetoothAdapter is null");
                }
                mLeHandle = -1;
                notifyAll();
            }
        }

        /**
         * Application interface registered - app is ready to go
         */
        public void onClientRegistered(int status, int clientIf) {
            if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status +
                           " clientIf=" + clientIf);
            synchronized(this) {
                if (mLeHandle == -1) {
                    if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled");
                }

                if (status == BluetoothGatt.GATT_SUCCESS) {
                    mLeHandle = clientIf;
                    IBluetoothGatt iGatt = null;
                    try {
                        BluetoothAdapter adapter = mBluetoothAdapter.get();
                        if (adapter != null) {
                            iGatt = adapter.getBluetoothManager().getBluetoothGatt();
                            if (mScanFilter == null) {
                                iGatt.startScan(mLeHandle, false);
                            } else {
                                ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length];
                                for(int i = 0; i != uuids.length; ++i) {
                                    uuids[i] = new ParcelUuid(mScanFilter[i]);
                                }
                                iGatt.startScanWithUuids(mLeHandle, false, uuids);
                            }
                        } else {
                            Log.e(TAG, "onClientRegistered, BluetoothAdapter null");
                            mLeHandle = -1;
                        }
                    } catch (RemoteException e) {
                        Log.e(TAG, "fail to start le scan: " + e);
                        mLeHandle = -1;
                    }
                    if (mLeHandle == -1) {
                        // registration succeeded but start scan or advertise failed
                        if (iGatt != null) {
                            try {
                                iGatt.unregisterClient(mLeHandle);
                            } catch (RemoteException e) {
                                Log.e(TAG, "fail to unregister callback: " + mLeHandle +
                                      " error: " + e);
                            }
                        }
                    }
                } else {
                    // registration failed
                    mLeHandle = -1;
                }
                notifyAll();
            }
        }

        public void onClientConnectionState(int status, int clientIf,
                                            boolean connected, String address) {
            // no op
        }

        /**
         * Callback reporting an LE scan result.
         * @hide
         */
        public void onScanResult(String address, int rssi, byte[] advData) {
            if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);

            // Check null in case the scan has been stopped
            synchronized(this) {
                if (mLeHandle <= 0) return;
            }
            try {
                BluetoothAdapter adapter = mBluetoothAdapter.get();
                if (adapter == null) {
                    Log.d(TAG, "onScanResult, BluetoothAdapter null");
        synchronized (mLeScanClients) {
            ScanCallback scanCallback = mLeScanClients.remove(callback);
            if (scanCallback == null) {
                if (DBG) Log.d(TAG, "scan not started yet");
                return;
            }
                mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData);
            } catch (Exception ex) {
                Log.w(TAG, "Unhandled exception: " + ex);
            }
        }

        public void onGetService(String address, int srvcType,
                                 int srvcInstId, ParcelUuid srvcUuid) {
            // no op
        }

        public void onGetIncludedService(String address, int srvcType,
                                         int srvcInstId, ParcelUuid srvcUuid,
                                         int inclSrvcType, int inclSrvcInstId,
                                         ParcelUuid inclSrvcUuid) {
            // no op
        }

        public void onGetCharacteristic(String address, int srvcType,
                                        int srvcInstId, ParcelUuid srvcUuid,
                                        int charInstId, ParcelUuid charUuid,
                                        int charProps) {
            // no op
        }

        public void onGetDescriptor(String address, int srvcType,
                                    int srvcInstId, ParcelUuid srvcUuid,
                                    int charInstId, ParcelUuid charUuid,
                                    int descInstId, ParcelUuid descUuid) {
            // no op
        }

        public void onSearchComplete(String address, int status) {
            // no op
        }

        public void onCharacteristicRead(String address, int status, int srvcType,
                                         int srvcInstId, ParcelUuid srvcUuid,
                                         int charInstId, ParcelUuid charUuid, byte[] value) {
            // no op
        }

        public void onCharacteristicWrite(String address, int status, int srvcType,
                                          int srvcInstId, ParcelUuid srvcUuid,
                                          int charInstId, ParcelUuid charUuid) {
            // no op
        }

        public void onNotify(String address, int srvcType,
                             int srvcInstId, ParcelUuid srvcUuid,
                             int charInstId, ParcelUuid charUuid,
                             byte[] value) {
            // no op
        }

        public void onDescriptorRead(String address, int status, int srvcType,
                                     int srvcInstId, ParcelUuid srvcUuid,
                                     int charInstId, ParcelUuid charUuid,
                                     int descInstId, ParcelUuid descrUuid, byte[] value) {
            // no op
        }

        public void onDescriptorWrite(String address, int status, int srvcType,
                                      int srvcInstId, ParcelUuid srvcUuid,
                                      int charInstId, ParcelUuid charUuid,
                                      int descInstId, ParcelUuid descrUuid) {
            // no op
        }

        public void onExecuteWrite(String address, int status) {
            // no op
        }

        public void onReadRemoteRssi(String address, int rssi, int status) {
            // no op
        }

        public void onAdvertiseStateChange(int advertiseState, int status) {
        }

        @Override
        public void onMultiAdvertiseCallback(int status) {
            // no op
        }

        @Override
        public void onConfigureMTU(String address, int mtu, int status) {
            // no op
        }

        @Override
        public void onConnectionCongested(String address, boolean congested) {
            // no op
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            // no op
        }

        @Override
        public void onFoundOrLost(boolean onFound, String address,int rssi,
                byte[] advData) {
            // no op
            scanner.stopScan(scanCallback);
        }
    }
}
+2 −4
Original line number Diff line number Diff line
@@ -33,10 +33,8 @@ import android.bluetooth.IBluetoothGattServerCallback;
interface IBluetoothGatt {
    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);

    void startScan(in int appIf, in boolean isServer);
    void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids);
    void startScanWithFilters(in int appIf, in boolean isServer,
                              in ScanSettings settings, in List<ScanFilter> filters);
    void startScan(in int appIf, in boolean isServer, in ScanSettings settings,
                   in List<ScanFilter> filters);
    void stopScan(in int appIf, in boolean isServer);
    void flushPendingBatchResults(in int appIf, in boolean isServer);
    void startMultiAdvertising(in int appIf,
+2 −1
Original line number Diff line number Diff line
@@ -151,6 +151,7 @@ public final class BluetoothLeScanner {
        synchronized (mLeScanClients) {
            BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback);
            if (wrapper == null) {
                if (DBG) Log.d(TAG, "could not find callback wrapper");
                return;
            }
            wrapper.stopLeScan();
@@ -266,7 +267,7 @@ public final class BluetoothLeScanner {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    mClientIf = clientIf;
                    try {
                        mBluetoothGatt.startScanWithFilters(mClientIf, false, mSettings, mFilters);
                        mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters);
                    } catch (RemoteException e) {
                        Log.e(TAG, "fail to start le scan: " + e);
                        mClientIf = -1;