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

Commit 59468ea6 authored by Ugo Yu's avatar Ugo Yu Committed by android-build-merger
Browse files

Merge "Refine Bluetooth Metadata API"

am: 61b2175c

Change-Id: I8b7e9137f67dfa27dd1556f49bdd4cc702015150
parents b64a3ad7 61b2175c
Loading
Loading
Loading
Loading
+65 −51
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ package android.bluetooth;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -37,7 +38,6 @@ import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -61,6 +61,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@@ -650,7 +651,7 @@ public final class BluetoothAdapter {

    private final Object mLock = new Object();
    private final Map<LeScanCallback, ScanCallback> mLeScanClients;
    private static final Map<BluetoothDevice, List<Pair<MetadataListener, Handler>>>
    private static final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>>
                sMetadataListeners = new HashMap<>();

    /**
@@ -660,14 +661,15 @@ public final class BluetoothAdapter {
    private static final IBluetoothMetadataListener sBluetoothMetadataListener =
            new IBluetoothMetadataListener.Stub() {
        @Override
        public void onMetadataChanged(BluetoothDevice device, int key, String value) {
        public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) {
            synchronized (sMetadataListeners) {
                if (sMetadataListeners.containsKey(device)) {
                    List<Pair<MetadataListener, Handler>> list = sMetadataListeners.get(device);
                    for (Pair<MetadataListener, Handler> pair : list) {
                        MetadataListener listener = pair.first;
                        Handler handler = pair.second;
                        handler.post(() -> {
                    List<Pair<OnMetadataChangedListener, Executor>> list =
                            sMetadataListeners.get(device);
                    for (Pair<OnMetadataChangedListener, Executor> pair : list) {
                        OnMetadataChangedListener listener = pair.first;
                        Executor executor = pair.second;
                        executor.execute(() -> {
                            listener.onMetadataChanged(device, key, value);
                        });
                    }
@@ -3153,30 +3155,30 @@ public final class BluetoothAdapter {
    }

    /**
     * Register a {@link #MetadataListener} to receive update about metadata
     * Register a {@link #OnMetadataChangedListener} to receive update about metadata
     * changes for this {@link BluetoothDevice}.
     * Registration must be done when Bluetooth is ON and will last until
     * {@link #unregisterMetadataListener(BluetoothDevice)} is called, even when Bluetooth
     * {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when Bluetooth
     * restarted in the middle.
     * All input parameters should not be null or {@link NullPointerException} will be triggered.
     * The same {@link BluetoothDevice} and {@link #MetadataListener} pair can only be registered
     * once, double registration would cause {@link IllegalArgumentException}.
     * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be
     * registered once, double registration would cause {@link IllegalArgumentException}.
     *
     * @param device {@link BluetoothDevice} that will be registered
     * @param listener {@link #MetadataListener} that will receive asynchronous callbacks
     * @param handler the handler for listener callback
     * @param executor the executor for listener callback
     * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks
     * @return true on success, false on error
     * @throws NullPointerException If one of {@code listener}, {@code device} or {@code handler}
     * @throws NullPointerException If one of {@code listener}, {@code device} or {@code executor}
     * is null.
     * @throws IllegalArgumentException The same {@link #MetadataListener} and
     * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and
     * {@link BluetoothDevice} are registered twice.
     * @hide
     */
    @SystemApi
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    public boolean registerMetadataListener(BluetoothDevice device, MetadataListener listener,
            Handler handler) {
        if (DBG) Log.d(TAG, "registerMetdataListener()");
    public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device,
            @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) {
        if (DBG) Log.d(TAG, "addOnMetadataChangedListener()");

        final IBluetooth service = mService;
        if (service == null) {
@@ -3189,14 +3191,15 @@ public final class BluetoothAdapter {
        if (device == null) {
            throw new NullPointerException("device is null");
        }
        if (handler == null) {
            throw new NullPointerException("handler is null");
        if (executor == null) {
            throw new NullPointerException("executor is null");
        }

        synchronized (sMetadataListeners) {
            List<Pair<MetadataListener, Handler>> listenerList = sMetadataListeners.get(device);
            List<Pair<OnMetadataChangedListener, Executor>> listenerList =
                    sMetadataListeners.get(device);
            if (listenerList == null) {
                // Create new listener/handler list for registeration
                // Create new listener/executor list for registeration
                listenerList = new ArrayList<>();
                sMetadataListeners.put(device, listenerList);
            } else {
@@ -3207,7 +3210,7 @@ public final class BluetoothAdapter {
                }
            }

            Pair<MetadataListener, Handler> listenerPair = new Pair(listener, handler);
            Pair<OnMetadataChangedListener, Executor> listenerPair = new Pair(listener, executor);
            listenerList.add(listenerPair);

            boolean ret = false;
@@ -3230,34 +3233,43 @@ public final class BluetoothAdapter {
    }

    /**
     * Unregister all {@link MetadataListener} from this {@link BluetoothDevice}.
     * Unregister a {@link #OnMetadataChangedListener} from a registered {@link BluetoothDevice}.
     * Unregistration can be done when Bluetooth is either ON or OFF.
     * {@link #registerMetadataListener(MetadataListener, BluetoothDevice, Handler)} must
     * be called before unregisteration.
     * Unregistering a device that is not regestered would cause {@link IllegalArgumentException}.
     * {@link #addOnMetadataChangedListener(OnMetadataChangedListener, BluetoothDevice, Executor)}
     * must be called before unregisteration.
     *
     * @param device {@link BluetoothDevice} that will be unregistered. it
     * @param device {@link BluetoothDevice} that will be unregistered. It
     * should not be null or {@link NullPointerException} will be triggered.
     * @param listener {@link OnMetadataChangedListener} that will be unregistered. It
     * should not be null or {@link NullPointerException} will be triggered.
     * @return true on success, false on error
     * @throws NullPointerException If {@code device} is null.
     * @throws NullPointerException If {@code listener} or {@code device} is null.
     * @throws IllegalArgumentException If {@code device} has not been registered before.
     * @hide
     */
    @SystemApi
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    public boolean unregisterMetadataListener(BluetoothDevice device) {
        if (DBG) Log.d(TAG, "unregisterMetdataListener()");
    public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device,
            @NonNull OnMetadataChangedListener listener) {
        if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()");
        if (device == null) {
            throw new NullPointerException("device is null");
        }
        if (listener == null) {
            throw new NullPointerException("listener is null");
        }

        synchronized (sMetadataListeners) {
            if (sMetadataListeners.containsKey(device)) {
                sMetadataListeners.remove(device);
            } else {
            if (!sMetadataListeners.containsKey(device)) {
                throw new IllegalArgumentException("device was not registered");
            }
            // Remove issued listener from the registered device
            sMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener)));

            if (sMetadataListeners.get(device).isEmpty()) {
                // Unregister to Bluetooth service if all listeners are removed from
                // the registered device
                sMetadataListeners.remove(device);
                final IBluetooth service = mService;
                if (service == null) {
                    // Bluetooth is OFF, do nothing to Bluetooth service.
@@ -3271,22 +3283,24 @@ public final class BluetoothAdapter {
                }
            }
        }
        return true;
    }

    /**
     * This abstract class is used to implement {@link BluetoothAdapter} metadata listener.
     * This interface is used to implement {@link BluetoothAdapter} metadata listener.
     * @hide
     */
    @SystemApi
    public abstract static class MetadataListener {
    public interface OnMetadataChangedListener {
        /**
         * Callback triggered if the metadata of {@link BluetoothDevice} registered in
         * {@link #registerMetadataListener}.
         * {@link #addOnMetadataChangedListener}.
         *
         * @param device changed {@link BluetoothDevice}.
         * @param key changed metadata key, one of BluetoothDevice.METADATA_*.
         * @param value the new value of metadata.
         * @param value the new value of metadata as byte array.
         */
        public void onMetadataChanged(BluetoothDevice device, int key, String value) {
        }
        void onMetadataChanged(@NonNull BluetoothDevice device, int key,
                @Nullable byte[] value);
    }
}
+43 −24
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.bluetooth;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -351,6 +352,7 @@ public final class BluetoothDevice implements Parcelable {

    /**
     * Manufacturer name of this Bluetooth device
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     */
    @SystemApi
@@ -358,6 +360,7 @@ public final class BluetoothDevice implements Parcelable {

    /**
     * Model name of this Bluetooth device
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     */
    @SystemApi
@@ -365,6 +368,7 @@ public final class BluetoothDevice implements Parcelable {

    /**
     * Software version of this Bluetooth device
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     */
    @SystemApi
@@ -372,6 +376,7 @@ public final class BluetoothDevice implements Parcelable {

    /**
     * Hardware version of this Bluetooth device
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     */
    @SystemApi
@@ -379,6 +384,7 @@ public final class BluetoothDevice implements Parcelable {

    /**
     * Package name of the companion app, if any
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     */
    @SystemApi
@@ -386,6 +392,7 @@ public final class BluetoothDevice implements Parcelable {

    /**
     * URI to the main icon shown on the settings UI
     * Data type should be {@link Byte} array.
     * @hide
     */
    @SystemApi
@@ -393,80 +400,91 @@ public final class BluetoothDevice implements Parcelable {

    /**
     * Whether this device is an untethered headset with left, right and case
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     */
    @SystemApi
    public static final int METADATA_IS_UNTHETHERED_HEADSET = 6;
    public static final int METADATA_IS_UNTETHERED_HEADSET = 6;

    /**
     * URI to icon of the left headset
     * Data type should be {@link Byte} array.
     * @hide
     */
    @SystemApi
    public static final int METADATA_UNTHETHERED_LEFT_ICON = 7;
    public static final int METADATA_UNTETHERED_LEFT_ICON = 7;

    /**
     * URI to icon of the right headset
     * Data type should be {@link Byte} array.
     * @hide
     */
    @SystemApi
    public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8;
    public static final int METADATA_UNTETHERED_RIGHT_ICON = 8;

    /**
     * URI to icon of the headset charging case
     * Data type should be {@link Byte} array.
     * @hide
     */
    @SystemApi
    public static final int METADATA_UNTHETHERED_CASE_ICON = 9;
    public static final int METADATA_UNTETHERED_CASE_ICON = 9;

    /**
     * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
     * is invalid, of the left headset
     * Battery level of left headset
     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
     * as invalid.
     * @hide
     */
    @SystemApi
    public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10;
    public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10;

    /**
     * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
     * is invalid, of the right headset
     * Battery level of rigth headset
     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
     * as invalid.
     * @hide
     */
    @SystemApi
    public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11;
    public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11;

    /**
     * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
     * is invalid, of the headset charging case
     * Battery level of the headset charging case
     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
     * as invalid.
     * @hide
     */
    @SystemApi
    public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12;
    public static final int METADATA_UNTETHERED_CASE_BATTERY = 12;

    /**
     * Whether the left headset is charging
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     */
    @SystemApi
    public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13;
    public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13;

    /**
     * Whether the right headset is charging
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     */
    @SystemApi
    public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14;
    public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14;

    /**
     * Whether the headset charging case is charging
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     */
    @SystemApi
    public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15;
    public static final int METADATA_UNTETHERED_CASE_CHARGING = 15;

    /**
     * URI to the enhanced settings UI slice, null or empty String means
     * the UI does not exist
     * URI to the enhanced settings UI slice
     * Data type should be {@String} as {@link Byte} array, null means
     * the UI does not exist.
     * @hide
     */
    @SystemApi
@@ -2243,21 +2261,21 @@ public final class BluetoothDevice implements Parcelable {
     * {@link #BOND_NONE}.
     *
     * @param key must be within the list of BluetoothDevice.METADATA_*
     * @param value the string data to set for key. Must be less than
     * @param value a byte array data to set for key. Must be less than
     * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length
     * @return true on success, false on error
     * @hide
    */
    @SystemApi
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    public boolean setMetadata(int key, String value) {
    public boolean setMetadata(int key, @NonNull byte[] value) {
        final IBluetooth service = sService;
        if (service == null) {
            Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata");
            return false;
        }
        if (value.length() > METADATA_MAX_LENGTH) {
            throw new IllegalArgumentException("value length is " + value.length()
        if (value.length > METADATA_MAX_LENGTH) {
            throw new IllegalArgumentException("value length is " + value.length
                    + ", should not over " + METADATA_MAX_LENGTH);
        }
        try {
@@ -2272,12 +2290,13 @@ public final class BluetoothDevice implements Parcelable {
     * Get a keyed metadata for this {@link BluetoothDevice} as {@link String}
     *
     * @param key must be within the list of BluetoothDevice.METADATA_*
     * @return Metadata of the key as string, null on error or not found
     * @return Metadata of the key as byte array, null on error or not found
     * @hide
     */
    @SystemApi
    @Nullable
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    public String getMetadata(int key) {
    public byte[] getMetadata(int key) {
        final IBluetooth service = sService;
        if (service == null) {
            Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata");