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

Commit 61b2175c authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Refine Bluetooth Metadata API"

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


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


    private final Object mLock = new Object();
    private final Object mLock = new Object();
    private final Map<LeScanCallback, ScanCallback> mLeScanClients;
    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<>();
                sMetadataListeners = new HashMap<>();


    /**
    /**
@@ -660,14 +661,15 @@ public final class BluetoothAdapter {
    private static final IBluetoothMetadataListener sBluetoothMetadataListener =
    private static final IBluetoothMetadataListener sBluetoothMetadataListener =
            new IBluetoothMetadataListener.Stub() {
            new IBluetoothMetadataListener.Stub() {
        @Override
        @Override
        public void onMetadataChanged(BluetoothDevice device, int key, String value) {
        public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) {
            synchronized (sMetadataListeners) {
            synchronized (sMetadataListeners) {
                if (sMetadataListeners.containsKey(device)) {
                if (sMetadataListeners.containsKey(device)) {
                    List<Pair<MetadataListener, Handler>> list = sMetadataListeners.get(device);
                    List<Pair<OnMetadataChangedListener, Executor>> list =
                    for (Pair<MetadataListener, Handler> pair : list) {
                            sMetadataListeners.get(device);
                        MetadataListener listener = pair.first;
                    for (Pair<OnMetadataChangedListener, Executor> pair : list) {
                        Handler handler = pair.second;
                        OnMetadataChangedListener listener = pair.first;
                        handler.post(() -> {
                        Executor executor = pair.second;
                        executor.execute(() -> {
                            listener.onMetadataChanged(device, key, value);
                            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}.
     * changes for this {@link BluetoothDevice}.
     * Registration must be done when Bluetooth is ON and will last until
     * 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.
     * restarted in the middle.
     * All input parameters should not be null or {@link NullPointerException} will be triggered.
     * 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
     * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be
     * once, double registration would cause {@link IllegalArgumentException}.
     * registered once, double registration would cause {@link IllegalArgumentException}.
     *
     *
     * @param device {@link BluetoothDevice} that will be registered
     * @param device {@link BluetoothDevice} that will be registered
     * @param listener {@link #MetadataListener} that will receive asynchronous callbacks
     * @param executor the executor for listener callback
     * @param handler the handler for listener callback
     * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks
     * @return true on success, false on error
     * @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.
     * is null.
     * @throws IllegalArgumentException The same {@link #MetadataListener} and
     * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and
     * {@link BluetoothDevice} are registered twice.
     * {@link BluetoothDevice} are registered twice.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @SystemApi
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    public boolean registerMetadataListener(BluetoothDevice device, MetadataListener listener,
    public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device,
            Handler handler) {
            @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) {
        if (DBG) Log.d(TAG, "registerMetdataListener()");
        if (DBG) Log.d(TAG, "addOnMetadataChangedListener()");


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


        synchronized (sMetadataListeners) {
        synchronized (sMetadataListeners) {
            List<Pair<MetadataListener, Handler>> listenerList = sMetadataListeners.get(device);
            List<Pair<OnMetadataChangedListener, Executor>> listenerList =
                    sMetadataListeners.get(device);
            if (listenerList == null) {
            if (listenerList == null) {
                // Create new listener/handler list for registeration
                // Create new listener/executor list for registeration
                listenerList = new ArrayList<>();
                listenerList = new ArrayList<>();
                sMetadataListeners.put(device, listenerList);
                sMetadataListeners.put(device, listenerList);
            } else {
            } 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);
            listenerList.add(listenerPair);


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


        synchronized (sMetadataListeners) {
        synchronized (sMetadataListeners) {
            if (sMetadataListeners.containsKey(device)) {
            if (!sMetadataListeners.containsKey(device)) {
                sMetadataListeners.remove(device);
            } else {
                throw new IllegalArgumentException("device was not registered");
                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;
                final IBluetooth service = mService;
                if (service == null) {
                if (service == null) {
                    // Bluetooth is OFF, do nothing to Bluetooth service.
                    // 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
     * @hide
     */
     */
    @SystemApi
    @SystemApi
    public abstract static class MetadataListener {
    public interface OnMetadataChangedListener {
        /**
        /**
         * Callback triggered if the metadata of {@link BluetoothDevice} registered in
         * Callback triggered if the metadata of {@link BluetoothDevice} registered in
         * {@link #registerMetadataListener}.
         * {@link #addOnMetadataChangedListener}.
         *
         *
         * @param device changed {@link BluetoothDevice}.
         * @param device changed {@link BluetoothDevice}.
         * @param key changed metadata key, one of BluetoothDevice.METADATA_*.
         * @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 Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package android.bluetooth;


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


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


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


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


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


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


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


    /**
    /**
     * Whether this device is an untethered headset with left, right and case
     * Whether this device is an untethered headset with left, right and case
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @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
     * URI to icon of the left headset
     * Data type should be {@link Byte} array.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @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
     * URI to icon of the right headset
     * Data type should be {@link Byte} array.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @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
     * URI to icon of the headset charging case
     * Data type should be {@link Byte} array.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @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}
     * Battery level of left headset
     * is invalid, of the left headset
     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
     * as invalid.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @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}
     * Battery level of rigth headset
     * is invalid, of the right headset
     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
     * as invalid.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @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}
     * Battery level of the headset charging case
     * is invalid, of the headset charging case
     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
     * as invalid.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @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
     * Whether the left headset is charging
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @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
     * Whether the right headset is charging
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @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
     * Whether the headset charging case is charging
     * Data type should be {@String} as {@link Byte} array.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @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
     * URI to the enhanced settings UI slice
     * the UI does not exist
     * Data type should be {@String} as {@link Byte} array, null means
     * the UI does not exist.
     * @hide
     * @hide
     */
     */
    @SystemApi
    @SystemApi
@@ -2243,21 +2261,21 @@ public final class BluetoothDevice implements Parcelable {
     * {@link #BOND_NONE}.
     * {@link #BOND_NONE}.
     *
     *
     * @param key must be within the list of BluetoothDevice.METADATA_*
     * @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
     * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length
     * @return true on success, false on error
     * @return true on success, false on error
     * @hide
     * @hide
    */
    */
    @SystemApi
    @SystemApi
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    public boolean setMetadata(int key, String value) {
    public boolean setMetadata(int key, @NonNull byte[] value) {
        final IBluetooth service = sService;
        final IBluetooth service = sService;
        if (service == null) {
        if (service == null) {
            Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata");
            Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata");
            return false;
            return false;
        }
        }
        if (value.length() > METADATA_MAX_LENGTH) {
        if (value.length > METADATA_MAX_LENGTH) {
            throw new IllegalArgumentException("value length is " + value.length()
            throw new IllegalArgumentException("value length is " + value.length
                    + ", should not over " + METADATA_MAX_LENGTH);
                    + ", should not over " + METADATA_MAX_LENGTH);
        }
        }
        try {
        try {
@@ -2272,12 +2290,13 @@ public final class BluetoothDevice implements Parcelable {
     * Get a keyed metadata for this {@link BluetoothDevice} as {@link String}
     * Get a keyed metadata for this {@link BluetoothDevice} as {@link String}
     *
     *
     * @param key must be within the list of BluetoothDevice.METADATA_*
     * @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
     * @hide
     */
     */
    @SystemApi
    @SystemApi
    @Nullable
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    public String getMetadata(int key) {
    public byte[] getMetadata(int key) {
        final IBluetooth service = sService;
        final IBluetooth service = sService;
        if (service == null) {
        if (service == null) {
            Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata");
            Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata");