Merge changes from topic "bt-metadata-api-v2-qt-dev" into qt-dev
* changes: Bluetooth metadata API changes in SettingsLib Refine Bluetooth Metadata API
This commit is contained in:
committed by
Android (Google) Code Review
commit
10093916cc
@@ -1242,30 +1242,29 @@ package android.app.usage {
|
||||
package android.bluetooth {
|
||||
|
||||
public final class BluetoothAdapter {
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
|
||||
method public boolean disableBLE();
|
||||
method public boolean enableBLE();
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect();
|
||||
method public boolean isBleScanAlwaysAvailable();
|
||||
method public boolean isLeEnabled();
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean registerMetadataListener(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothAdapter.MetadataListener, android.os.Handler);
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean unregisterMetadataListener(android.bluetooth.BluetoothDevice);
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
|
||||
field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
|
||||
field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
|
||||
}
|
||||
|
||||
public abstract static class BluetoothAdapter.MetadataListener {
|
||||
ctor public BluetoothAdapter.MetadataListener();
|
||||
method public void onMetadataChanged(android.bluetooth.BluetoothDevice, int, String);
|
||||
public static interface BluetoothAdapter.OnMetadataChangedListener {
|
||||
method public void onMetadataChanged(@NonNull android.bluetooth.BluetoothDevice, int, @Nullable byte[]);
|
||||
}
|
||||
|
||||
public final class BluetoothDevice implements android.os.Parcelable {
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess();
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int);
|
||||
method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode();
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, String);
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, @NonNull byte[]);
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
|
||||
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSilenceMode(boolean);
|
||||
field public static final int ACCESS_ALLOWED = 1; // 0x1
|
||||
@@ -1275,21 +1274,21 @@ package android.bluetooth {
|
||||
field public static final int METADATA_COMPANION_APP = 4; // 0x4
|
||||
field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10
|
||||
field public static final int METADATA_HARDWARE_VERSION = 3; // 0x3
|
||||
field public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; // 0x6
|
||||
field public static final int METADATA_IS_UNTETHERED_HEADSET = 6; // 0x6
|
||||
field public static final int METADATA_MAIN_ICON = 5; // 0x5
|
||||
field public static final int METADATA_MANUFACTURER_NAME = 0; // 0x0
|
||||
field public static final int METADATA_MAX_LENGTH = 2048; // 0x800
|
||||
field public static final int METADATA_MODEL_NAME = 1; // 0x1
|
||||
field public static final int METADATA_SOFTWARE_VERSION = 2; // 0x2
|
||||
field public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; // 0xc
|
||||
field public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; // 0xf
|
||||
field public static final int METADATA_UNTHETHERED_CASE_ICON = 9; // 0x9
|
||||
field public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; // 0xa
|
||||
field public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; // 0xd
|
||||
field public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; // 0x7
|
||||
field public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; // 0xb
|
||||
field public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; // 0xe
|
||||
field public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; // 0x8
|
||||
field public static final int METADATA_UNTETHERED_CASE_BATTERY = 12; // 0xc
|
||||
field public static final int METADATA_UNTETHERED_CASE_CHARGING = 15; // 0xf
|
||||
field public static final int METADATA_UNTETHERED_CASE_ICON = 9; // 0x9
|
||||
field public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10; // 0xa
|
||||
field public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13; // 0xd
|
||||
field public static final int METADATA_UNTETHERED_LEFT_ICON = 7; // 0x7
|
||||
field public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11; // 0xb
|
||||
field public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14; // 0xe
|
||||
field public static final int METADATA_UNTETHERED_RIGHT_ICON = 8; // 0x8
|
||||
}
|
||||
|
||||
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
|
||||
|
||||
@@ -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,63 +3233,74 @@ 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)));
|
||||
|
||||
final IBluetooth service = mService;
|
||||
if (service == null) {
|
||||
// Bluetooth is OFF, do nothing to Bluetooth service.
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
return service.unregisterMetadataListener(device);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "unregisterMetadataListener fail", e);
|
||||
return false;
|
||||
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.
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
return service.unregisterMetadataListener(device);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "unregisterMetadataListener fail", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -28,6 +28,8 @@ public class BluetoothUtils {
|
||||
public static final boolean V = false; // verbose logging
|
||||
public static final boolean D = true; // regular logging
|
||||
|
||||
public static final int META_INT_ERROR = -1;
|
||||
|
||||
private static ErrorListener sErrorListener;
|
||||
|
||||
public static int getConnectionStateSummary(int connectionState) {
|
||||
@@ -133,20 +135,16 @@ public class BluetoothUtils {
|
||||
final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
|
||||
context, cachedDevice);
|
||||
final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
|
||||
final boolean untetheredHeadset = bluetoothDevice != null
|
||||
? Boolean.parseBoolean(bluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))
|
||||
: false;
|
||||
final boolean untetheredHeadset = getBooleanMetaData(
|
||||
bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET);
|
||||
final int iconSize = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.bt_nearby_icon_size);
|
||||
final Resources resources = context.getResources();
|
||||
|
||||
// Deal with untethered headset
|
||||
if (untetheredHeadset) {
|
||||
final String uriString = bluetoothDevice != null
|
||||
? bluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)
|
||||
: null;
|
||||
final Uri iconUri = uriString != null ? Uri.parse(uriString) : null;
|
||||
final Uri iconUri = getUriMetaData(bluetoothDevice,
|
||||
BluetoothDevice.METADATA_MAIN_ICON);
|
||||
if (iconUri != null) {
|
||||
try {
|
||||
context.getContentResolver().takePersistableUriPermission(iconUri,
|
||||
@@ -194,4 +192,77 @@ public class BluetoothUtils {
|
||||
|
||||
return adaptiveIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get boolean Bluetooth metadata
|
||||
*
|
||||
* @param bluetoothDevice the BluetoothDevice to get metadata
|
||||
* @param key key value within the list of BluetoothDevice.METADATA_*
|
||||
* @return the boolean metdata
|
||||
*/
|
||||
public static boolean getBooleanMetaData(BluetoothDevice bluetoothDevice, int key) {
|
||||
if (bluetoothDevice == null) {
|
||||
return false;
|
||||
}
|
||||
final byte[] data = bluetoothDevice.getMetadata(key);
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.parseBoolean(new String(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get String Bluetooth metadata
|
||||
*
|
||||
* @param bluetoothDevice the BluetoothDevice to get metadata
|
||||
* @param key key value within the list of BluetoothDevice.METADATA_*
|
||||
* @return the String metdata
|
||||
*/
|
||||
public static String getStringMetaData(BluetoothDevice bluetoothDevice, int key) {
|
||||
if (bluetoothDevice == null) {
|
||||
return null;
|
||||
}
|
||||
final byte[] data = bluetoothDevice.getMetadata(key);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get integer Bluetooth metadata
|
||||
*
|
||||
* @param bluetoothDevice the BluetoothDevice to get metadata
|
||||
* @param key key value within the list of BluetoothDevice.METADATA_*
|
||||
* @return the int metdata
|
||||
*/
|
||||
public static int getIntMetaData(BluetoothDevice bluetoothDevice, int key) {
|
||||
if (bluetoothDevice == null) {
|
||||
return META_INT_ERROR;
|
||||
}
|
||||
final byte[] data = bluetoothDevice.getMetadata(key);
|
||||
if (data == null) {
|
||||
return META_INT_ERROR;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(new String(data));
|
||||
} catch (NumberFormatException e) {
|
||||
return META_INT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URI Bluetooth metadata
|
||||
*
|
||||
* @param bluetoothDevice the BluetoothDevice to get metadata
|
||||
* @param key key value within the list of BluetoothDevice.METADATA_*
|
||||
* @return the URI metdata
|
||||
*/
|
||||
public static Uri getUriMetaData(BluetoothDevice bluetoothDevice, int key) {
|
||||
String data = getStringMetaData(bluetoothDevice, key);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
return Uri.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -881,16 +881,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
||||
//when profile is connected, information would be available
|
||||
if (profileConnected) {
|
||||
// Update Meta data for connected device
|
||||
if (Boolean.parseBoolean(
|
||||
mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))) {
|
||||
try {
|
||||
leftBattery = Integer.parseInt(
|
||||
mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY));
|
||||
rightBattery = Integer.parseInt(mDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY));
|
||||
} catch (NumberFormatException e) {
|
||||
Log.d(TAG, "Parse error for unthethered battery level.");
|
||||
}
|
||||
if (BluetoothUtils.getBooleanMetaData(
|
||||
mDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) {
|
||||
leftBattery = BluetoothUtils.getIntMetaData(mDevice,
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY);
|
||||
rightBattery = BluetoothUtils.getIntMetaData(mDevice,
|
||||
BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY);
|
||||
}
|
||||
|
||||
// Set default string with battery level in device connected situation.
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.settingslib.widget.AdaptiveIcon;
|
||||
@@ -47,6 +48,9 @@ public class BluetoothUtilsTest {
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
|
||||
private Context mContext;
|
||||
private static final String STRING_METADATA = "string_metadata";
|
||||
private static final String BOOL_METADATA = "true";
|
||||
private static final String INT_METADATA = "25";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -78,7 +82,7 @@ public class BluetoothUtilsTest {
|
||||
@Test
|
||||
public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn("false");
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn("false".getBytes());
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mCachedBluetoothDevice.getAddress()).thenReturn("1f:aa:bb");
|
||||
|
||||
@@ -86,4 +90,64 @@ public class BluetoothUtilsTest {
|
||||
RuntimeEnvironment.application,
|
||||
mCachedBluetoothDevice).first).isInstanceOf(AdaptiveIcon.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStringMetaData_hasMetaData_getCorrectMetaData() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)).thenReturn(
|
||||
STRING_METADATA.getBytes());
|
||||
|
||||
assertThat(BluetoothUtils.getStringMetaData(mBluetoothDevice,
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)).isEqualTo(STRING_METADATA);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIntMetaData_hasMetaData_getCorrectMetaData() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
|
||||
INT_METADATA.getBytes());
|
||||
|
||||
assertThat(BluetoothUtils.getIntMetaData(mBluetoothDevice,
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY))
|
||||
.isEqualTo(Integer.parseInt(INT_METADATA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIntMetaData_invalidMetaData_getErrorCode() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(null);
|
||||
|
||||
assertThat(BluetoothUtils.getIntMetaData(mBluetoothDevice,
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON))
|
||||
.isEqualTo(BluetoothUtils.META_INT_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBooleanMetaData_hasMetaData_getCorrectMetaData() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
|
||||
BOOL_METADATA.getBytes());
|
||||
|
||||
assertThat(BluetoothUtils.getBooleanMetaData(mBluetoothDevice,
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getUriMetaData_hasMetaData_getCorrectMetaData() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(
|
||||
STRING_METADATA.getBytes());
|
||||
|
||||
assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice,
|
||||
BluetoothDevice.METADATA_MAIN_ICON)).isEqualTo(Uri.parse(STRING_METADATA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getUriMetaData_nullMetaData_getNullUri() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(null);
|
||||
|
||||
assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice,
|
||||
BluetoothDevice.METADATA_MAIN_ICON)).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,12 +455,12 @@ public class CachedBluetoothDeviceTest {
|
||||
updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
|
||||
when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn(
|
||||
"true");
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn(
|
||||
TWS_BATTERY_LEFT);
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY)).thenReturn(
|
||||
TWS_BATTERY_RIGHT);
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
|
||||
"true".getBytes());
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
|
||||
TWS_BATTERY_LEFT.getBytes());
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
|
||||
TWS_BATTERY_RIGHT.getBytes());
|
||||
|
||||
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
|
||||
"Active, L: 15% battery, R: 25% battery");
|
||||
@@ -472,12 +472,12 @@ public class CachedBluetoothDeviceTest {
|
||||
updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
|
||||
updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
|
||||
when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn(
|
||||
"true");
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn(
|
||||
TWS_BATTERY_LEFT);
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY)).thenReturn(
|
||||
TWS_BATTERY_RIGHT);
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
|
||||
"true".getBytes());
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
|
||||
TWS_BATTERY_LEFT.getBytes());
|
||||
when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
|
||||
TWS_BATTERY_RIGHT.getBytes());
|
||||
|
||||
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
|
||||
"L: 15% battery, R: 25% battery");
|
||||
|
||||
Reference in New Issue
Block a user