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

Commit 01e8e2ac authored by Matthew Xie's avatar Matthew Xie
Browse files

Remove BluetoothAdapterCallback. Simplify leScan Api

App does not need to explicitly register/unregister callback
bug 8599881

Change-Id: I18cfef14d7ddb344722945e657dcb959823b412b
parent 6b79decb
Loading
Loading
Loading
Loading
+257 −165
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.ParcelUuid;
@@ -30,11 +29,14 @@ import android.util.Log;
import android.util.Pair;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
@@ -357,9 +359,7 @@ public final class BluetoothAdapter {
    private final IBluetoothManager mManagerService;
    private IBluetooth mService;

    private Handler mServiceRecordHandler;
    private BluetoothAdapterCallback mCallback;
    private int mClientIf;
    private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;

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

    /**
@@ -1409,72 +1409,38 @@ public final class BluetoothAdapter {
    }

    /**
     * Register an callback to receive async results, such as LE scan result.
     * Callback interface used to deliver LE scan results.
     *
     * <p>This is an asynchronous call. The callback
     * {@link BluetoothAdapterCallback#onCallbackRegistration}
     * is used to notify success or failure if the function returns true.
     *
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
     *
     * @param callback BluetootAdapter callback handler that will receive asynchronous callbacks.
     * @return If true, the callback will be called to notify success or failure,
     *         false on immediate error
     * @see #startLeScan(LeScanCallback)
     * @see #startLeScan(UUID[], LeScanCallback)
     */
    public boolean registerCallback(BluetoothAdapterCallback callback) {
        try {
            IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
            mCallback = callback;
            UUID uuid = UUID.randomUUID();
            if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);

            iGatt.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
            return true;
        } catch (RemoteException e) {
            Log.e(TAG,"",e);
            return false;
        }
    }

    public interface LeScanCallback {
        /**
     * Unregister the registered callback.
         * Callback reporting an LE device found during a device scan initiated
         * by the {@link BluetoothAdapter#startLeScan} function.
         *
         * @param device Identifies the remote device
         * @param rssi The RSSI value for the remote device as reported by the
         *             Bluetooth hardware. 0 if no RSSI value is available.
         * @param scanRecord The content of the advertisement record offered by
         *                   the remote device.
         */
    public boolean unRegisterCallback(BluetoothAdapterCallback callback) {
        if (callback != mCallback) return false;
        try {
            IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();

            iGatt.unregisterClient(mClientIf);
            return true;
        } catch (RemoteException e) {
            Log.e(TAG,"",e);
            return false;
        }
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
    }

    /**
     * Starts a scan for Bluetooth LE devices.
     *
     * <p>Results of the scan are reported using the
     * {@link BluetoothAdapterCallback#onLeScan} callback.
     * {@link LeScanCallback#onLeScan} callback.
     *
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
     *
     * @param callback the callback LE scan results are delivered
     * @return true, if the scan was started successfully
     */
    public boolean startLeScan() {
        if (DBG) Log.d(TAG, "startLeScan()");
        if (mClientIf == 0) return false;

        try {
            IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
            iGatt.startScan(mClientIf, false);
        } catch (RemoteException e) {
            Log.e(TAG,"",e);
            return false;
        }

        return true;
    public boolean startLeScan(LeScanCallback callback) {
        return startLeScan(null, callback);
    }

    /**
@@ -1482,64 +1448,181 @@ public final class BluetoothAdapter {
     * advertise given services.
     *
     * <p>Devices which advertise all specified services are reported using the
     * {@link BluetoothAdapterCallback#onLeScan} callback.
     * {@link LeScanCallback#onLeScan} callback.
     *
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
     *
     * @param serviceUuids Array of services to look for
     * @param callback the callback LE scan results are delivered
     * @return true, if the scan was started successfully
     */
    public boolean startLeScan(UUID[] serviceUuids) {
        if (DBG) Log.d(TAG, "startLeScan() - with UUIDs");
        if (mClientIf == 0) return false;
    public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) {
        if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids);

        try {
            IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
            ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
            for(int i = 0; i != uuids.length; ++i) {
                uuids[i] = new ParcelUuid(serviceUuids[i]);
        if (callback == null) {
            if (DBG) Log.e(TAG, "startLeScan: null callback");
            return false;
        }
            iGatt.startScanWithUuids(mClientIf, false, uuids);
        } catch (RemoteException e) {
            Log.e(TAG,"",e);

        synchronized(mLeScanClients) {
            if (mLeScanClients.containsKey(callback)) {
                if (DBG) Log.e(TAG, "LE Scan has already started");
                return false;
            }

            try {
                IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
                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;
                }
            } catch (RemoteException e) {
                Log.e(TAG,"",e);
            }
        }
        return false;
    }

    /**
     * Stops an ongoing Bluetooth LE device scan.
     *
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
     *
     * @param callback used to identify which scan to stop
     *        must be the same handle used to start the scan
     */
    public void stopLeScan() {
        if (DBG) Log.d(TAG, "stopScan()");
        if (mClientIf == 0) return;

        try {
            IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
            iGatt.stopScan(mClientIf, false);
        } catch (RemoteException e) {
            Log.e(TAG,"",e);
    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 final IBluetoothGattCallback mBluetoothGattCallback =
        new IBluetoothGattCallback.Stub() {
    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() {
            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 < LE_CALLBACK_REG_WAIT_COUNT) {
                    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);
                    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);
                mClientIf = clientIf;
                mCallback.onCallbackRegistration(status == BluetoothGatt.GATT_SUCCESS ?
                                  BluetoothAdapterCallback.CALLBACK_REGISTERED :
                                  BluetoothAdapterCallback.CALLBACK_REGISTRATION_FAILURE);
            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 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,
@@ -1554,8 +1637,17 @@ public final class BluetoothAdapter {
        public void onScanResult(String address, int rssi, byte[] advData) {
            if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);

            // Check null in case the scan has been stopped
            synchronized(this) {
                if (mLeHandle <= 0) return;
            }
            try {
                    mCallback.onLeScan(getRemoteDevice(address), rssi, advData);
                BluetoothAdapter adapter = mBluetoothAdapter.get();
                if (adapter == null) {
                    Log.d(TAG, "onScanResult, BluetoothAdapter null");
                    return;
                }
                mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData);
            } catch (Exception ex) {
                Log.w(TAG, "Unhandled exception: " + ex);
            }
@@ -1631,6 +1723,6 @@ public final class BluetoothAdapter {
        public void onReadRemoteRssi(String address, int rssi, int status) {
            // no op
        }
        };
    }

}
+0 −57
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.bluetooth;

import android.bluetooth.BluetoothDevice;

/**
 * This abstract class is used to implement {@link BluetoothAdapter} callbacks.
 */
public abstract class BluetoothAdapterCallback {

    /**
     * Indicates the callback has been registered successfully
     */
    public static final int CALLBACK_REGISTERED = 0;

    /**
     * Indicates the callback registration has failed
     */
    public static final int CALLBACK_REGISTRATION_FAILURE = 1;

    /**
     * Callback to inform change in registration state of the  application.
     *
     * @param status Returns {@link #CALLBACK_REGISTERED} if the application
     *               was successfully registered.
     */
    public void onCallbackRegistration(int status) {
    }

    /**
     * Callback reporting an LE device found during a device scan initiated
     * by the {@link BluetoothAdapter#startLeScan} function.
     *
     * @param device Identifies the remote device
     * @param rssi The RSSI value for the remote device as reported by the
     *             Bluetooth hardware. 0 if no RSSI value is available.
     * @param scanRecord The content of the advertisement record offered by
     *                   the remote device.
     */
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -127,7 +127,7 @@ public final class BluetoothManager {

        try {
            IBluetoothManager managerService = mAdapter.getBluetoothManager();
            IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
            IBluetoothGatt iGatt = managerService.getBluetoothGatt();
            if (iGatt == null) return connectedDevices;

            connectedDevices = iGatt.getDevicesMatchingConnectionStates(
@@ -172,7 +172,7 @@ public final class BluetoothManager {

        try {
            IBluetoothManager managerService = mAdapter.getBluetoothManager();
            IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
            IBluetoothGatt iGatt = managerService.getBluetoothGatt();
            if (iGatt == null) return devices;
            devices = iGatt.getDevicesMatchingConnectionStates(states);
        } catch (RemoteException e) {
@@ -203,7 +203,7 @@ public final class BluetoothManager {

        try {
            IBluetoothManager managerService = mAdapter.getBluetoothManager();
            IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
            IBluetoothGatt iGatt = managerService.getBluetoothGatt();
            if (iGatt == null) {
                Log.e(TAG, "Fail to get GATT Server connection");
                return null;