Loading framework/java/android/bluetooth/BluetoothAdapter.java +65 −51 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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<>(); /** /** Loading @@ -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); }); }); } } Loading Loading @@ -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) { Loading @@ -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 { Loading @@ -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; Loading @@ -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. Loading @@ -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); } } } } framework/java/android/bluetooth/BluetoothDevice.java +43 −24 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 { Loading @@ -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"); Loading Loading
framework/java/android/bluetooth/BluetoothAdapter.java +65 −51 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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<>(); /** /** Loading @@ -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); }); }); } } Loading Loading @@ -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) { Loading @@ -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 { Loading @@ -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; Loading @@ -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. Loading @@ -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); } } } }
framework/java/android/bluetooth/BluetoothDevice.java +43 −24 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 { Loading @@ -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"); Loading