summaryrefslogtreecommitdiff
path: root/framework/java/android/bluetooth/BluetoothLeBroadcast.java
diff options
context:
space:
mode:
authorJack He <siyuanh@google.com>2021-12-15 15:40:34 -0800
committerJack He <siyuanh@google.com>2022-02-02 15:17:34 -0800
commita014314c9490a18fd4b900302d873206df09a3e0 (patch)
treeb60b0f2f949032b8b4a9c16d28d61357d9ff1f3f /framework/java/android/bluetooth/BluetoothLeBroadcast.java
parentb99f7eced58e8e722366ce364a8a2eef4e4449e0 (diff)
Introduce LE audio broadcast system APIs
* Rename BluetoothLeBroadcastSourceInfo to BluetoothLeBroadcastReceiveState so that it matches the name in the Bluetooth specification * Added callbacks to BluetoothLeBroadcast so that caller that wait for asynchronouze operations with reason code in the hope to reduce potential race conditions * Allow multiple broadcast to be set up on the same deivce if the device supports it * Added ScanFilter to searchForSources() method and removed selectSources() method for BluetoothLeBroadcastAssistant so that the Bluetooth stack can automatically handle periodic sync after a Broadcast source is found and only do this for a limited number of devices * Added structural APIs to store Broadcast Source and Group information * Added unknown address type in BluetoothDevice Bug: 208222281 Test: make Tag: #feature Ignore-AOSP-First: Merge conflict in master Change-Id: If4c3af658b5bc1283d76e5d1899485a487ab7626 Merged-In: If4c3af658b5bc1283d76e5d1899485a487ab7626 (cherry picked from commit 4f9d902028fbe271167547884c33fb9ec7601080)
Diffstat (limited to 'framework/java/android/bluetooth/BluetoothLeBroadcast.java')
-rw-r--r--framework/java/android/bluetooth/BluetoothLeBroadcast.java460
1 files changed, 301 insertions, 159 deletions
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcast.java b/framework/java/android/bluetooth/BluetoothLeBroadcast.java
index fed9f911d5..4537cc6f97 100644
--- a/framework/java/android/bluetooth/BluetoothLeBroadcast.java
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcast.java
@@ -16,271 +16,413 @@
package android.bluetooth;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.content.Context;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
/**
- * This class provides the public APIs to control the Bluetooth LE Broadcast Source profile.
+ * This class provides the public APIs to control the BAP Broadcast Source profile.
*
- * <p>BluetoothLeBroadcast is a proxy object for controlling the Bluetooth LE Broadcast
- * Source Service via IPC. Use {@link BluetoothAdapter#getProfileProxy}
- * to get the BluetoothLeBroadcast proxy object.
+ * <p>BluetoothLeBroadcast is a proxy object for controlling the Bluetooth LE Broadcast Source
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothLeBroadcast
+ * proxy object.
*
* @hide
*/
-public final class BluetoothLeBroadcast implements BluetoothProfile {
+@SystemApi
+public final class BluetoothLeBroadcast implements AutoCloseable, BluetoothProfile {
private static final String TAG = "BluetoothLeBroadcast";
private static final boolean DBG = true;
- private static final boolean VDBG = false;
/**
- * Constants used by the LE Audio Broadcast profile for the Broadcast state
- *
- * @hide
- */
- @IntDef(prefix = {"LE_AUDIO_BROADCAST_STATE_"}, value = {
- LE_AUDIO_BROADCAST_STATE_DISABLED,
- LE_AUDIO_BROADCAST_STATE_ENABLING,
- LE_AUDIO_BROADCAST_STATE_ENABLED,
- LE_AUDIO_BROADCAST_STATE_DISABLING,
- LE_AUDIO_BROADCAST_STATE_PLAYING,
- LE_AUDIO_BROADCAST_STATE_NOT_PLAYING
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface LeAudioBroadcastState {}
-
- /**
- * Indicates that LE Audio Broadcast mode is currently disabled
- *
+ * Interface for receiving events related to Broadcast Source
* @hide
*/
- public static final int LE_AUDIO_BROADCAST_STATE_DISABLED = 10;
+ @SystemApi
+ public interface Callback {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothStatusCodes.ERROR_UNKNOWN,
+ BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST,
+ BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST,
+ BluetoothStatusCodes.REASON_SYSTEM_POLICY,
+ BluetoothStatusCodes.ERROR_HARDWARE_GENERIC,
+ BluetoothStatusCodes.ERROR_BAD_PARAMETERS,
+ BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES,
+ BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_CODE,
+ BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID,
+ BluetoothStatusCodes.ERROR_LE_CONTENT_METADATA_INVALID_PROGRAM_INFO,
+ BluetoothStatusCodes.ERROR_LE_CONTENT_METADATA_INVALID_LANGUAGE,
+ BluetoothStatusCodes.ERROR_LE_CONTENT_METADATA_INVALID_OTHER,
+ })
+ @interface Reason {}
- /**
- * Indicates that LE Audio Broadcast mode is being enabled
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_STATE_ENABLING = 11;
+ /**
+ * Callback invoked when broadcast is started, but audio may not be playing.
+ *
+ * Caller should wait for
+ * {@link #onBroadcastMetadataChanged(int, BluetoothLeBroadcastMetadata)}
+ * for the updated metadata
+ *
+ * @param reason for broadcast start
+ * @param broadcastId as defined by the Basic Audio Profile
+ * @hide
+ */
+ @SystemApi
+ void onBroadcastStarted(@Reason int reason, int broadcastId);
- /**
- * Indicates that LE Audio Broadcast mode is currently enabled
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_STATE_ENABLED = 12;
- /**
- * Indicates that LE Audio Broadcast mode is being disabled
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_STATE_DISABLING = 13;
+ /**
+ * Callback invoked when broadcast failed to start
+ *
+ * @param reason for broadcast start failure
+ * @hide
+ */
+ @SystemApi
+ void onBroadcastStartFailed(@Reason int reason);
- /**
- * Indicates that an LE Audio Broadcast mode is currently playing
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_STATE_PLAYING = 14;
+ /**
+ * Callback invoked when broadcast is stopped
+ *
+ * @param reason for broadcast stop
+ * @hide
+ */
+ @SystemApi
+ void onBroadcastStopped(@Reason int reason, int broadcastId);
- /**
- * Indicates that LE Audio Broadcast is currently not playing
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_STATE_NOT_PLAYING = 15;
+ /**
+ * Callback invoked when broadcast failed to stop
+ *
+ * @param reason for broadcast stop failure
+ * @hide
+ */
+ @SystemApi
+ void onBroadcastStopFailed(@Reason int reason);
- /**
- * Constants used by the LE Audio Broadcast profile for encryption key length
- *
- * @hide
- */
- @IntDef(prefix = {"LE_AUDIO_BROADCAST_ENCRYPTION_KEY_"}, value = {
- LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT,
- LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface LeAudioEncryptionKeyLength {}
+ /**
+ * Callback invoked when broadcast audio is playing
+ *
+ * @param reason for playback start
+ * @param broadcastId as defined by the Basic Audio Profile
+ * @hide
+ */
+ @SystemApi
+ void onPlaybackStarted(@Reason int reason, int broadcastId);
- /**
- * Indicates that the LE Audio Broadcast encryption key size is 32 bits.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT = 16;
+ /**
+ * Callback invoked when broadcast audio is not playing
+ *
+ * @param reason for playback stop
+ * @param broadcastId as defined by the Basic Audio Profile
+ * @hide
+ */
+ @SystemApi
+ void onPlaybackStopped(@Reason int reason, int broadcastId);
- /**
- * Indicates that the LE Audio Broadcast encryption key size is 128 bits.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT = 17;
+ /**
+ * Callback invoked when encryption is enabled
+ *
+ * @param reason for encryption enable
+ * @param broadcastId as defined by the Basic Audio Profile
+ * @hide
+ */
+ @SystemApi
+ void onBroadcastUpdated(@Reason int reason, int broadcastId);
- /**
- * Interface for receiving events related to broadcasts
- */
- public interface Callback {
/**
- * Called when broadcast state has changed
+ * Callback invoked when Broadcast Source failed to update
*
- * @param prevState broadcast state before the change
- * @param newState broadcast state after the change
+ * @param reason for update failure
+ * @param broadcastId as defined by the Basic Audio Profile
+ * @hide
*/
- @LeAudioBroadcastState
- void onBroadcastStateChange(int prevState, int newState);
+ @SystemApi
+ void onBroadcastUpdateFailed(int reason, int broadcastId);
+
/**
- * Called when encryption key has been updated
+ * Callback invoked when Broadcast Source metadata is updated
*
- * @param success true if the key was updated successfully, false otherwise
+ * @param metadata updated Broadcast Source metadata
+ * @param broadcastId as defined by the Basic Audio Profile
+ * @hide
*/
- void onEncryptionKeySet(boolean success);
+ @SystemApi
+ void onBroadcastMetadataChanged(int broadcastId,
+ @NonNull BluetoothLeBroadcastMetadata metadata);
}
/**
- * Create a BluetoothLeBroadcast proxy object for interacting with the local
- * LE Audio Broadcast Source service.
+ * Create a BluetoothLeBroadcast proxy object for interacting with the local LE Audio Broadcast
+ * Source service.
*
+ * @param context for to operate this API class
+ * @param listener listens for service callbacks across binder
* @hide
*/
- /*package*/ BluetoothLeBroadcast(Context context,
- BluetoothProfile.ServiceListener listener) {
- }
+ /*package*/ BluetoothLeBroadcast(Context context, BluetoothProfile.ServiceListener listener) {}
/**
- * Not supported since LE Audio Broadcasts do not establish a connection
- *
- * @throws UnsupportedOperationException
+ * Not supported since LE Audio Broadcasts do not establish a connection.
*
* @hide
*/
@Override
- public int getConnectionState(BluetoothDevice device) {
- throw new UnsupportedOperationException(
- "LE Audio Broadcasts are not connection-oriented.");
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public int getConnectionState(@NonNull BluetoothDevice device) {
+ throw new UnsupportedOperationException("LE Audio Broadcasts are not connection-oriented.");
}
/**
- * Not supported since LE Audio Broadcasts do not establish a connection
- *
- * @throws UnsupportedOperationException
+ * Not supported since LE Audio Broadcasts do not establish a connection.
*
* @hide
*/
@Override
- public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- throw new UnsupportedOperationException(
- "LE Audio Broadcasts are not connection-oriented.");
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ @NonNull int[] states) {
+ throw new UnsupportedOperationException("LE Audio Broadcasts are not connection-oriented.");
}
/**
- * Not supported since LE Audio Broadcasts do not establish a connection
- *
- * @throws UnsupportedOperationException
+ * Not supported since LE Audio Broadcasts do not establish a connection.
*
* @hide
*/
@Override
- public List<BluetoothDevice> getConnectedDevices() {
- throw new UnsupportedOperationException(
- "LE Audio Broadcasts are not connection-oriented.");
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
+ throw new UnsupportedOperationException("LE Audio Broadcasts are not connection-oriented.");
}
/**
- * Enable LE Audio Broadcast mode.
+ * Register a {@link Callback} that will be invoked during the operation of this profile.
*
- * Generates a new broadcast ID and enables sending of encrypted or unencrypted
- * isochronous PDUs
+ * Repeated registration of the same <var>callback</var> object will have no effect after
+ * the first call to this method, even when the <var>executor</var> is different. API caller
+ * would have to call {@link #unregisterCallback(Callback)} with
+ * the same callback object before registering it again.
*
+ * @param executor an {@link Executor} to execute given callback
+ * @param callback user implementation of the {@link Callback}
+ * @throws IllegalArgumentException if a null executor, sink, or callback is given
* @hide
*/
- public int enableBroadcastMode() {
- if (DBG) log("enableBroadcastMode");
- return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED;
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void registerCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Callback callback) {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor cannot be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ log("registerCallback");
+ throw new UnsupportedOperationException("Not Implemented");
}
/**
- * Disable LE Audio Broadcast mode.
+ * Unregister the specified {@link Callback}
+ * <p>The same {@link Callback} object used when calling
+ * {@link #registerCallback(Executor, Callback)} must be used.
+ *
+ * <p>Callbacks are automatically unregistered when application process goes away
*
+ * @param callback user implementation of the {@link Callback}
+ * @throws IllegalArgumentException when callback is null or when no callback is registered
* @hide
*/
- public int disableBroadcastMode() {
- if (DBG) log("disableBroadcastMode");
- return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED;
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void unregisterCallback(@NonNull Callback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ log("unregisterCallback");
+ throw new UnsupportedOperationException("Not Implemented");
}
/**
- * Get the current LE Audio broadcast state
+ * Start broadcasting to nearby devices using <var>broadcastCode</var> and
+ * <var>contentMetadata</var>
+ *
+ * Encryption will be enabled when <var>broadcastCode</var> is not null.
+ *
+ * <p>As defined in Volume 3, Part C, Section 3.2.6 of Bluetooth Core Specification, Version
+ * 5.3, Broadcast Code is used to encrypt a broadcast audio stream.
+ * <p>It must be a UTF-8 string that has at least 4 octets and should not exceed 16 octets.
*
+ * If the provided <var>broadcastCode</var> is non-null and does not meet the above
+ * requirements, encryption will fail to enable with reason code
+ * {@link BluetoothStatusCodes#ERROR_LE_BROADCAST_INVALID_CODE}
+ *
+ * Caller can set content metadata such as program information string in
+ * <var>contentMetadata</var>
+ *
+ * On success, {@link Callback#onBroadcastStarted(int, int)} will be invoked with
+ * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST} reason code.
+ * On failure, {@link Callback#onBroadcastStartFailed(int)} will be invoked with reason code.
+ *
+ * In particular, when the number of Broadcast Sources reaches
+ * {@link #getMaximumNumberOfBroadcast()}, this method will fail with
+ * {@link BluetoothStatusCodes#ERROR_LOCAL_NOT_ENOUGH_RESOURCES}
+ *
+ * After broadcast is started,
+ * {@link Callback#onBroadcastMetadataChanged(int, BluetoothLeBroadcastMetadata)}
+ * will be invoked to expose the latest Broadcast Group metadata that can be shared out of band
+ * to set up Broadcast Sink without scanning.
+ *
+ * Alternatively, one can also get the latest Broadcast Source meta via
+ * {@link #getAllBroadcastMetadata()}
+ *
+ * @param contentMetadata metadata for the default Broadcast subgroup
+ * @param broadcastCode Encryption will be enabled when <var>broadcastCode</var> is not null
+ * @throws IllegalArgumentException if <var>contentMetadata</var> is null
+ * @throws IllegalStateException if callback was not registered
* @hide
*/
- @LeAudioBroadcastState
- public int getBroadcastState() {
- if (DBG) log("getBroadcastState");
- return LE_AUDIO_BROADCAST_STATE_DISABLED;
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void startBroadcast(@NonNull BluetoothLeAudioContentMetadata contentMetadata,
+ @Nullable byte[] broadcastCode) {
+ if (DBG) log("startBroadcasting");
}
/**
- * Enable LE Audio broadcast encryption
+ * Update the broadcast with <var>broadcastId</var> with new <var>contentMetadata</var>
*
- * @param keyLength if useExisting is true, this specifies the length of the key that should
- * be generated
- * @param useExisting true, if an existing key should be used
- * false, if a new key should be generated
+ * On success, {@link Callback#onBroadcastUpdated(int, int)} will be invoked with reason code
+ * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}.
+ * On failure, {@link Callback#onBroadcastUpdateFailed(int, int)} will be invoked with reason
+ * code
*
+ * @param broadcastId broadcastId as defined by the Basic Audio Profile
+ * @param contentMetadata updated metadata for the default Broadcast subgroup
+ * @throws IllegalStateException if callback was not registered
* @hide
*/
- @LeAudioEncryptionKeyLength
- public int enableEncryption(boolean useExisting, int keyLength) {
- if (DBG) log("enableEncryption useExisting=" + useExisting + " keyLength=" + keyLength);
- return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED;
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void updateBroadcast(int broadcastId,
+ @NonNull BluetoothLeAudioContentMetadata contentMetadata) {
+
}
/**
- * Disable LE Audio broadcast encryption
+ * Stop broadcasting.
*
- * @param removeExisting true, if the existing key should be removed
- * false, otherwise
+ * On success, {@link Callback#onBroadcastStopped(int, int)} will be invoked with reason code
+ * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST} and the <var>broadcastId</var>
+ * On failure, {@link Callback#onBroadcastStopFailed(int)} will be invoked with reason code
*
+ * @param broadcastId as defined by the Basic Audio Profile
+ * @throws IllegalStateException if callback was not registered
* @hide
*/
- public int disableEncryption(boolean removeExisting) {
- if (DBG) log("disableEncryption removeExisting=" + removeExisting);
- return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED;
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void stopBroadcast(int broadcastId) {
+ if (DBG) log("disableBroadcastMode");
}
/**
- * Enable or disable LE Audio broadcast encryption
- *
- * @param key use the provided key if non-null, generate a new key if null
- * @param keyLength 0 if encryption is disabled, 4 bytes (low security),
- * 16 bytes (high security)
+ * Return true if audio is being broadcasted on the Broadcast Source as identified by the
+ * <var>broadcastId</var>
*
+ * @param broadcastId as defined in the Basic Audio Profile
+ * @return true if audio is being broadcasted
* @hide
*/
- @LeAudioEncryptionKeyLength
- public int setEncryptionKey(byte[] key, int keyLength) {
- if (DBG) log("setEncryptionKey key=" + key + " keyLength=" + keyLength);
- return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED;
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public boolean isPlaying(int broadcastId) {
+ return false;
}
-
/**
- * Get the encryption key that was set before
- *
- * @return encryption key as a byte array or null if no encryption key was set
+ * Get {@link BluetoothLeBroadcastMetadata} for all Broadcast Groups currently running on
+ * this device
*
+ * @return list of {@link BluetoothLeBroadcastMetadata}
* @hide
*/
- public byte[] getEncryptionKey() {
- if (DBG) log("getEncryptionKey");
- return null;
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @NonNull List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
+ return Collections.emptyList();
}
+ /**
+ * Get the maximum number of broadcast groups supported on this device
+ * @return maximum number of broadcast groups supported on this device
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public int getMaximumNumberOfBroadcast() {
+ return 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void close() throws Exception {}
+
private static void log(String msg) {
Log.d(TAG, msg);
}