Loading framework/java/android/bluetooth/BluetoothAdapter.java +175 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ 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; Loading Loading @@ -648,6 +649,32 @@ 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>>> sMetadataListeners = new HashMap<>(); /** * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener * implementation. */ private static final IBluetoothMetadataListener sBluetoothMetadataListener = new IBluetoothMetadataListener.Stub() { @Override public void onMetadataChanged(BluetoothDevice device, int key, String 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(() -> { listener.onMetadataChanged(device, key, value); }); } } } return; } }; /** * Get a handle to the default local Bluetooth adapter. Loading Loading @@ -2607,6 +2634,16 @@ public final class BluetoothAdapter { } } } synchronized (sMetadataListeners) { sMetadataListeners.forEach((device, pair) -> { try { mService.registerMetadataListener(sBluetoothMetadataListener, device); } catch (RemoteException e) { Log.e(TAG, "Failed to register metadata listener", e); } }); } } public void onBluetoothServiceDown() { Loading Loading @@ -3090,4 +3127,142 @@ public final class BluetoothAdapter { + "listenUsingInsecureL2capChannel"); return listenUsingInsecureL2capChannel(); } /** * Register a {@link #MetadataListener} 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 * 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}. * * @param device {@link BluetoothDevice} that will be registered * @param listener {@link #MetadataListener} that will receive asynchronous callbacks * @param handler the handler for listener callback * @return true on success, false on error * @throws NullPointerException If one of {@code listener}, {@code device} or {@code handler} * is null. * @throws IllegalArgumentException The same {@link #MetadataListener} 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()"); final IBluetooth service = mService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot register metadata listener"); return false; } if (listener == null) { throw new NullPointerException("listener is null"); } if (device == null) { throw new NullPointerException("device is null"); } if (handler == null) { throw new NullPointerException("handler is null"); } synchronized (sMetadataListeners) { List<Pair<MetadataListener, Handler>> listenerList = sMetadataListeners.get(device); if (listenerList == null) { // Create new listener/handler list for registeration listenerList = new ArrayList<>(); sMetadataListeners.put(device, listenerList); } else { // Check whether this device was already registed by the lisenter if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) { throw new IllegalArgumentException("listener was already regestered" + " for the device"); } } Pair<MetadataListener, Handler> listenerPair = new Pair(listener, handler); listenerList.add(listenerPair); boolean ret = false; try { ret = service.registerMetadataListener(sBluetoothMetadataListener, device); } catch (RemoteException e) { Log.e(TAG, "registerMetadataListener fail", e); } finally { if (!ret) { // Remove listener registered earlier when fail. listenerList.remove(listenerPair); if (listenerList.isEmpty()) { // Remove the device if its listener list is empty sMetadataListeners.remove(device); } } } return ret; } } /** * Unregister all {@link MetadataListener} from this {@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}. * * @param device {@link BluetoothDevice} 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 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()"); if (device == null) { throw new NullPointerException("device is null"); } synchronized (sMetadataListeners) { if (sMetadataListeners.containsKey(device)) { sMetadataListeners.remove(device); } else { throw new IllegalArgumentException("device was not registered"); } 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; } } } /** * This abstract class is used to implement {@link BluetoothAdapter} metadata listener. * @hide */ @SystemApi public abstract class MetadataListener { /** * Callback triggered if the metadata of {@link BluetoothDevice} registered in * {@link #registerMetadataListener}. * * @param device changed {@link BluetoothDevice}. * @param key changed metadata key, one of BluetoothDevice.METADATA_*. * @param value the new value of metadata. */ public void onMetadataChanged(BluetoothDevice device, int key, String value) { } } } framework/java/android/bluetooth/BluetoothDevice.java +188 −0 Original line number Diff line number Diff line Loading @@ -340,6 +340,137 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; /** * Maximum length of a metadata entry, this is to avoid exploding Bluetooth * disk usage * @hide */ @SystemApi public static final int METADATA_MAX_LENGTH = 2048; /** * Manufacturer name of this Bluetooth device * @hide */ @SystemApi public static final int METADATA_MANUFACTURER_NAME = 0; /** * Model name of this Bluetooth device * @hide */ @SystemApi public static final int METADATA_MODEL_NAME = 1; /** * Software version of this Bluetooth device * @hide */ @SystemApi public static final int METADATA_SOFTWARE_VERSION = 2; /** * Hardware version of this Bluetooth device * @hide */ @SystemApi public static final int METADATA_HARDWARE_VERSION = 3; /** * Package name of the companion app, if any * @hide */ @SystemApi public static final int METADATA_COMPANION_APP = 4; /** * URI to the main icon shown on the settings UI * @hide */ @SystemApi public static final int METADATA_MAIN_ICON = 5; /** * Whether this device is an untethered headset with left, right and case * @hide */ @SystemApi public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; /** * URI to icon of the left headset * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; /** * URI to icon of the right headset * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; /** * URI to icon of the headset charging case * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_CASE_ICON = 9; /** * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} * is invalid, of the left headset * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; /** * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} * is invalid, of the right headset * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; /** * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} * is invalid, of the headset charging case * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; /** * Whether the left headset is charging * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; /** * Whether the right headset is charging * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; /** * Whether the headset charging case is charging * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; /** * URI to the enhanced settings UI slice, null or empty String means * the UI does not exist * @hide */ @SystemApi public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; /** * Broadcast Action: This intent is used to broadcast the {@link UUID} * wrapped as a {@link android.os.ParcelUuid} of the remote device after it Loading Loading @@ -2026,4 +2157,61 @@ public final class BluetoothDevice implements Parcelable { Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel"); return createInsecureL2capChannel(psm); } /** * Set a keyed metadata of this {@link BluetoothDevice} to a * {@link String} value. * Only bonded devices's metadata will be persisted across Bluetooth * restart. * Metadata will be removed when the device's bond state is moved to * {@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 * {@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) { 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() + ", should not over " + METADATA_MAX_LENGTH); } try { return service.setMetadata(this, key, value); } catch (RemoteException e) { Log.e(TAG, "setMetadata fail", e); return false; } } /** * 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 * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int key) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata"); return null; } try { return service.getMetadata(this, key); } catch (RemoteException e) { Log.e(TAG, "getMetadata fail", e); return null; } } } Loading
framework/java/android/bluetooth/BluetoothAdapter.java +175 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ 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; Loading Loading @@ -648,6 +649,32 @@ 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>>> sMetadataListeners = new HashMap<>(); /** * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener * implementation. */ private static final IBluetoothMetadataListener sBluetoothMetadataListener = new IBluetoothMetadataListener.Stub() { @Override public void onMetadataChanged(BluetoothDevice device, int key, String 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(() -> { listener.onMetadataChanged(device, key, value); }); } } } return; } }; /** * Get a handle to the default local Bluetooth adapter. Loading Loading @@ -2607,6 +2634,16 @@ public final class BluetoothAdapter { } } } synchronized (sMetadataListeners) { sMetadataListeners.forEach((device, pair) -> { try { mService.registerMetadataListener(sBluetoothMetadataListener, device); } catch (RemoteException e) { Log.e(TAG, "Failed to register metadata listener", e); } }); } } public void onBluetoothServiceDown() { Loading Loading @@ -3090,4 +3127,142 @@ public final class BluetoothAdapter { + "listenUsingInsecureL2capChannel"); return listenUsingInsecureL2capChannel(); } /** * Register a {@link #MetadataListener} 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 * 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}. * * @param device {@link BluetoothDevice} that will be registered * @param listener {@link #MetadataListener} that will receive asynchronous callbacks * @param handler the handler for listener callback * @return true on success, false on error * @throws NullPointerException If one of {@code listener}, {@code device} or {@code handler} * is null. * @throws IllegalArgumentException The same {@link #MetadataListener} 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()"); final IBluetooth service = mService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot register metadata listener"); return false; } if (listener == null) { throw new NullPointerException("listener is null"); } if (device == null) { throw new NullPointerException("device is null"); } if (handler == null) { throw new NullPointerException("handler is null"); } synchronized (sMetadataListeners) { List<Pair<MetadataListener, Handler>> listenerList = sMetadataListeners.get(device); if (listenerList == null) { // Create new listener/handler list for registeration listenerList = new ArrayList<>(); sMetadataListeners.put(device, listenerList); } else { // Check whether this device was already registed by the lisenter if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) { throw new IllegalArgumentException("listener was already regestered" + " for the device"); } } Pair<MetadataListener, Handler> listenerPair = new Pair(listener, handler); listenerList.add(listenerPair); boolean ret = false; try { ret = service.registerMetadataListener(sBluetoothMetadataListener, device); } catch (RemoteException e) { Log.e(TAG, "registerMetadataListener fail", e); } finally { if (!ret) { // Remove listener registered earlier when fail. listenerList.remove(listenerPair); if (listenerList.isEmpty()) { // Remove the device if its listener list is empty sMetadataListeners.remove(device); } } } return ret; } } /** * Unregister all {@link MetadataListener} from this {@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}. * * @param device {@link BluetoothDevice} 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 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()"); if (device == null) { throw new NullPointerException("device is null"); } synchronized (sMetadataListeners) { if (sMetadataListeners.containsKey(device)) { sMetadataListeners.remove(device); } else { throw new IllegalArgumentException("device was not registered"); } 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; } } } /** * This abstract class is used to implement {@link BluetoothAdapter} metadata listener. * @hide */ @SystemApi public abstract class MetadataListener { /** * Callback triggered if the metadata of {@link BluetoothDevice} registered in * {@link #registerMetadataListener}. * * @param device changed {@link BluetoothDevice}. * @param key changed metadata key, one of BluetoothDevice.METADATA_*. * @param value the new value of metadata. */ public void onMetadataChanged(BluetoothDevice device, int key, String value) { } } }
framework/java/android/bluetooth/BluetoothDevice.java +188 −0 Original line number Diff line number Diff line Loading @@ -340,6 +340,137 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; /** * Maximum length of a metadata entry, this is to avoid exploding Bluetooth * disk usage * @hide */ @SystemApi public static final int METADATA_MAX_LENGTH = 2048; /** * Manufacturer name of this Bluetooth device * @hide */ @SystemApi public static final int METADATA_MANUFACTURER_NAME = 0; /** * Model name of this Bluetooth device * @hide */ @SystemApi public static final int METADATA_MODEL_NAME = 1; /** * Software version of this Bluetooth device * @hide */ @SystemApi public static final int METADATA_SOFTWARE_VERSION = 2; /** * Hardware version of this Bluetooth device * @hide */ @SystemApi public static final int METADATA_HARDWARE_VERSION = 3; /** * Package name of the companion app, if any * @hide */ @SystemApi public static final int METADATA_COMPANION_APP = 4; /** * URI to the main icon shown on the settings UI * @hide */ @SystemApi public static final int METADATA_MAIN_ICON = 5; /** * Whether this device is an untethered headset with left, right and case * @hide */ @SystemApi public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; /** * URI to icon of the left headset * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; /** * URI to icon of the right headset * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; /** * URI to icon of the headset charging case * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_CASE_ICON = 9; /** * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} * is invalid, of the left headset * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; /** * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} * is invalid, of the right headset * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; /** * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} * is invalid, of the headset charging case * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; /** * Whether the left headset is charging * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; /** * Whether the right headset is charging * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; /** * Whether the headset charging case is charging * @hide */ @SystemApi public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; /** * URI to the enhanced settings UI slice, null or empty String means * the UI does not exist * @hide */ @SystemApi public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; /** * Broadcast Action: This intent is used to broadcast the {@link UUID} * wrapped as a {@link android.os.ParcelUuid} of the remote device after it Loading Loading @@ -2026,4 +2157,61 @@ public final class BluetoothDevice implements Parcelable { Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel"); return createInsecureL2capChannel(psm); } /** * Set a keyed metadata of this {@link BluetoothDevice} to a * {@link String} value. * Only bonded devices's metadata will be persisted across Bluetooth * restart. * Metadata will be removed when the device's bond state is moved to * {@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 * {@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) { 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() + ", should not over " + METADATA_MAX_LENGTH); } try { return service.setMetadata(this, key, value); } catch (RemoteException e) { Log.e(TAG, "setMetadata fail", e); return false; } } /** * 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 * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int key) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata"); return null; } try { return service.getMetadata(this, key); } catch (RemoteException e) { Log.e(TAG, "getMetadata fail", e); return null; } } }