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
diff --git a/framework/api/current.txt b/framework/api/current.txt
index c7f671a..1e18dc7 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -527,6 +527,7 @@
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_UUID = "android.bluetooth.device.action.UUID";
field public static final int ADDRESS_TYPE_PUBLIC = 0; // 0x0
field public static final int ADDRESS_TYPE_RANDOM = 1; // 0x1
+ field public static final int ADDRESS_TYPE_UNKNOWN = 65535; // 0xffff
field public static final int BOND_BONDED = 12; // 0xc
field public static final int BOND_BONDING = 11; // 0xb
field public static final int BOND_NONE = 10; // 0xa
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 69d4c8d..b4dd386 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -203,6 +203,183 @@
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioLocation(@NonNull android.bluetooth.BluetoothDevice);
}
+ public final class BluetoothLeAudioCodecConfigMetadata implements android.os.Parcelable {
+ method @NonNull public static android.bluetooth.BluetoothLeAudioCodecConfigMetadata fromRawBytes(@NonNull byte[]);
+ method public long getAudioLocation();
+ method @NonNull public byte[] getRawMetadata();
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeAudioCodecConfigMetadata> CREATOR;
+ }
+
+ public static final class BluetoothLeAudioCodecConfigMetadata.Builder {
+ ctor public BluetoothLeAudioCodecConfigMetadata.Builder();
+ ctor public BluetoothLeAudioCodecConfigMetadata.Builder(@NonNull android.bluetooth.BluetoothLeAudioCodecConfigMetadata);
+ method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfigMetadata build();
+ method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfigMetadata.Builder setAudioLocation(long);
+ }
+
+ public final class BluetoothLeAudioContentMetadata implements android.os.Parcelable {
+ method @NonNull public static android.bluetooth.BluetoothLeAudioContentMetadata fromRawBytes(@NonNull byte[]);
+ method @Nullable public String getLanguage();
+ method @Nullable public String getProgramInfo();
+ method @NonNull public byte[] getRawMetadata();
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeAudioContentMetadata> CREATOR;
+ }
+
+ public static final class BluetoothLeAudioContentMetadata.Builder {
+ ctor public BluetoothLeAudioContentMetadata.Builder();
+ ctor public BluetoothLeAudioContentMetadata.Builder(@NonNull android.bluetooth.BluetoothLeAudioContentMetadata);
+ method @NonNull public android.bluetooth.BluetoothLeAudioContentMetadata build();
+ method @NonNull public android.bluetooth.BluetoothLeAudioContentMetadata.Builder setLanguage(@Nullable String);
+ method @NonNull public android.bluetooth.BluetoothLeAudioContentMetadata.Builder setProgramInfo(@Nullable String);
+ }
+
+ public final class BluetoothLeBroadcast implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothLeBroadcastMetadata> getAllBroadcastMetadata();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getMaximumNumberOfBroadcast();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isPlaying(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothLeBroadcast.Callback);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void startBroadcast(@NonNull android.bluetooth.BluetoothLeAudioContentMetadata, @Nullable byte[]);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void stopBroadcast(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void unregisterCallback(@NonNull android.bluetooth.BluetoothLeBroadcast.Callback);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void updateBroadcast(int, @NonNull android.bluetooth.BluetoothLeAudioContentMetadata);
+ }
+
+ public static interface BluetoothLeBroadcast.Callback {
+ method public void onBroadcastMetadataChanged(int, @NonNull android.bluetooth.BluetoothLeBroadcastMetadata);
+ method public void onBroadcastStartFailed(int);
+ method public void onBroadcastStarted(int, int);
+ method public void onBroadcastStopFailed(int);
+ method public void onBroadcastStopped(int, int);
+ method public void onBroadcastUpdateFailed(int, int);
+ method public void onBroadcastUpdated(int, int);
+ method public void onPlaybackStarted(int, int);
+ method public void onPlaybackStopped(int, int);
+ }
+
+ public final class BluetoothLeBroadcastAssistant implements android.bluetooth.BluetoothProfile {
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void addSource(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothLeBroadcastMetadata, boolean);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothLeBroadcastReceiveState> getAllSources(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ method public int getMaximumSourceCapacity(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isSearchInProgress();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void modifySource(@NonNull android.bluetooth.BluetoothDevice, int, @NonNull android.bluetooth.BluetoothLeBroadcastMetadata);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothLeBroadcastAssistant.Callback);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void removeSource(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void startSearchingForSources(@NonNull java.util.List<android.bluetooth.le.ScanFilter>);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void stopSearchingForSources();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void unregisterCallback(@NonNull android.bluetooth.BluetoothLeBroadcastAssistant.Callback);
+ field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.action.CONNECTION_STATE_CHANGED";
+ }
+
+ public static interface BluetoothLeBroadcastAssistant.Callback {
+ method public void onReceiveStateChanged(@NonNull android.bluetooth.BluetoothDevice, int, @NonNull android.bluetooth.BluetoothLeBroadcastReceiveState);
+ method public void onSearchStartFailed(int);
+ method public void onSearchStarted(int);
+ method public void onSearchStopFailed(int);
+ method public void onSearchStopped(int);
+ method public void onSourceAddFailed(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothLeBroadcastMetadata, int);
+ method public void onSourceAdded(@NonNull android.bluetooth.BluetoothDevice, int, int);
+ method public void onSourceFound(@NonNull android.bluetooth.BluetoothLeBroadcastMetadata);
+ method public void onSourceModified(@NonNull android.bluetooth.BluetoothDevice, int, int);
+ method public void onSourceModifyFailed(@NonNull android.bluetooth.BluetoothDevice, int, int);
+ method public void onSourceRemoveFailed(@NonNull android.bluetooth.BluetoothDevice, int, int);
+ method public void onSourceRemoved(@NonNull android.bluetooth.BluetoothDevice, int, int);
+ }
+
+ public final class BluetoothLeBroadcastChannel implements android.os.Parcelable {
+ method public int getChannelIndex();
+ method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfigMetadata getCodecMetadata();
+ method public boolean isSelected();
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeBroadcastChannel> CREATOR;
+ }
+
+ public static final class BluetoothLeBroadcastChannel.Builder {
+ ctor public BluetoothLeBroadcastChannel.Builder();
+ ctor public BluetoothLeBroadcastChannel.Builder(@NonNull android.bluetooth.BluetoothLeBroadcastChannel);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastChannel build();
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastChannel.Builder setChannelIndex(int);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastChannel.Builder setCodecMetadata(@NonNull android.bluetooth.BluetoothLeAudioCodecConfigMetadata);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastChannel.Builder setSelected(boolean);
+ }
+
+ public final class BluetoothLeBroadcastMetadata implements android.os.Parcelable {
+ method @Nullable public byte[] getBroadcastCode();
+ method public int getBroadcastId();
+ method public int getPaSyncInterval();
+ method @IntRange(from=0, to=16777215) public int getPresentationDelayMicros();
+ method public int getSourceAddressType();
+ method public int getSourceAdvertisingSid();
+ method @Nullable public android.bluetooth.BluetoothDevice getSourceDevice();
+ method @NonNull public java.util.List<android.bluetooth.BluetoothLeBroadcastSubgroup> getSubgroups();
+ method public boolean isEncrypted();
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeBroadcastMetadata> CREATOR;
+ field public static final int PA_SYNC_INTERVAL_UNKNOWN = 65535; // 0xffff
+ }
+
+ public static final class BluetoothLeBroadcastMetadata.Builder {
+ ctor public BluetoothLeBroadcastMetadata.Builder();
+ ctor public BluetoothLeBroadcastMetadata.Builder(@NonNull android.bluetooth.BluetoothLeBroadcastMetadata);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder addSubgroup(@NonNull android.bluetooth.BluetoothLeBroadcastSubgroup);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata build();
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder clearSubgroup();
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setBroadcastCode(@Nullable byte[]);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setBroadcastId(int);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setEncrypted(boolean);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setPaSyncInterval(int);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setPresentationDelayMicros(@IntRange(from=0, to=16777215) int);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setSourceAdvertisingSid(int);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setSourceDevice(@Nullable android.bluetooth.BluetoothDevice, int);
+ }
+
+ public final class BluetoothLeBroadcastReceiveState implements android.os.Parcelable {
+ method @Nullable public byte[] getBadCode();
+ method public int getBigEncryptionState();
+ method @NonNull public java.util.List<java.lang.Long> getBisSyncState();
+ method public int getBroadcastId();
+ method public int getNumSubgroups();
+ method public int getPaSyncState();
+ method public int getSourceAddressType();
+ method public int getSourceAdvertisingSid();
+ method @NonNull public android.bluetooth.BluetoothDevice getSourceDevice();
+ method @IntRange(from=0, to=255) public int getSourceId();
+ method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioContentMetadata> getSubgroupMetadata();
+ field public static final int BIG_ENCRYPTION_STATE_BAD_CODE = 3; // 0x3
+ field public static final int BIG_ENCRYPTION_STATE_CODE_REQUIRED = 1; // 0x1
+ field public static final int BIG_ENCRYPTION_STATE_DECRYPTING = 2; // 0x2
+ field public static final int BIG_ENCRYPTION_STATE_NOT_ENCRYPTED = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeBroadcastReceiveState> CREATOR;
+ field public static final int PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE = 3; // 0x3
+ field public static final int PA_SYNC_STATE_IDLE = 0; // 0x0
+ field public static final int PA_SYNC_STATE_NO_PAST = 4; // 0x4
+ field public static final int PA_SYNC_STATE_SYNCHRONIZED = 2; // 0x2
+ field public static final int PA_SYNC_STATE_SYNCINFO_REQUEST = 1; // 0x1
+ }
+
+ public final class BluetoothLeBroadcastSubgroup implements android.os.Parcelable {
+ method @NonNull public java.util.List<android.bluetooth.BluetoothLeBroadcastChannel> getChannels();
+ method public long getCodecId();
+ method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfigMetadata getCodecSpecificConfig();
+ method @NonNull public android.bluetooth.BluetoothLeAudioContentMetadata getContentMetadata();
+ method public boolean isNoChannelPreference();
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeBroadcastSubgroup> CREATOR;
+ }
+
+ public static final class BluetoothLeBroadcastSubgroup.Builder {
+ ctor public BluetoothLeBroadcastSubgroup.Builder();
+ ctor public BluetoothLeBroadcastSubgroup.Builder(@NonNull android.bluetooth.BluetoothLeBroadcastSubgroup);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup.Builder addChannel(@NonNull android.bluetooth.BluetoothLeBroadcastChannel);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup build();
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup.Builder clearChannel();
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup.Builder setCodecId(long);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup.Builder setCodecSpecificConfig(@NonNull android.bluetooth.BluetoothLeAudioCodecConfigMetadata);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup.Builder setContentMetadata(@NonNull android.bluetooth.BluetoothLeAudioContentMetadata);
+ method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup.Builder setNoChannelPreference(boolean);
+ }
+
public final class BluetoothMap implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
method public void close();
method protected void finalize();
@@ -263,6 +440,8 @@
field public static final int CONNECTION_POLICY_FORBIDDEN = 0; // 0x0
field public static final int CONNECTION_POLICY_UNKNOWN = -1; // 0xffffffff
field public static final int HEADSET_CLIENT = 16; // 0x10
+ field public static final int LE_AUDIO_BROADCAST = 26; // 0x1a
+ field public static final int LE_AUDIO_BROADCAST_ASSISTANT = 29; // 0x1d
field public static final int MAP_CLIENT = 18; // 0x12
field public static final int PAN = 5; // 0x5
field public static final int PBAP_CLIENT = 17; // 0x11
@@ -273,16 +452,34 @@
public final class BluetoothStatusCodes {
field public static final int ALLOWED = 400; // 0x190
+ field public static final int ERROR_ALREADY_IN_TARGET_STATE = 26; // 0x1a
field public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; // 0x3e8
field public static final int ERROR_AUDIO_DEVICE_ALREADY_CONNECTED = 1116; // 0x45c
field public static final int ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED = 1117; // 0x45d
field public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118; // 0x45e
+ field public static final int ERROR_BAD_PARAMETERS = 21; // 0x15
field public static final int ERROR_CALL_ACTIVE = 1119; // 0x45f
+ field public static final int ERROR_HARDWARE_GENERIC = 20; // 0x14
+ field public static final int ERROR_LE_BROADCAST_ASSISTANT_DUPLICATE_ADDITION = 1203; // 0x4b3
+ field public static final int ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID = 1202; // 0x4b2
+ field public static final int ERROR_LE_BROADCAST_INVALID_BROADCAST_ID = 1200; // 0x4b0
+ field public static final int ERROR_LE_BROADCAST_INVALID_CODE = 1201; // 0x4b1
+ field public static final int ERROR_LE_CONTENT_METADATA_INVALID_LANGUAGE = 1205; // 0x4b5
+ field public static final int ERROR_LE_CONTENT_METADATA_INVALID_OTHER = 1206; // 0x4b6
+ field public static final int ERROR_LE_CONTENT_METADATA_INVALID_PROGRAM_INFO = 1204; // 0x4b4
+ field public static final int ERROR_LOCAL_NOT_ENOUGH_RESOURCES = 22; // 0x16
field public static final int ERROR_NOT_ACTIVE_DEVICE = 12; // 0xc
field public static final int ERROR_NO_ACTIVE_DEVICES = 13; // 0xd
field public static final int ERROR_PROFILE_NOT_CONNECTED = 14; // 0xe
+ field public static final int ERROR_REMOTE_LINK_ERROR = 25; // 0x19
+ field public static final int ERROR_REMOTE_NOT_ENOUGH_RESOURCES = 23; // 0x17
+ field public static final int ERROR_REMOTE_OPERATION_REJECTED = 24; // 0x18
field public static final int ERROR_TIMEOUT = 15; // 0xf
field public static final int NOT_ALLOWED = 401; // 0x191
+ field public static final int REASON_LOCAL_APP_REQUEST = 16; // 0x10
+ field public static final int REASON_LOCAL_STACK_REQUEST = 17; // 0x11
+ field public static final int REASON_REMOTE_REQUEST = 18; // 0x12
+ field public static final int REASON_SYSTEM_POLICY = 19; // 0x13
field public static final int RFCOMM_LISTENER_FAILED_TO_CLOSE_SERVER_SOCKET = 2004; // 0x7d4
field public static final int RFCOMM_LISTENER_FAILED_TO_CREATE_SERVER_SOCKET = 2003; // 0x7d3
field public static final int RFCOMM_LISTENER_NO_SOCKET_AVAILABLE = 2005; // 0x7d5
diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java
index c43a519..4c254aa 100644
--- a/framework/java/android/bluetooth/BluetoothDevice.java
+++ b/framework/java/android/bluetooth/BluetoothDevice.java
@@ -1132,6 +1132,8 @@
ADDRESS_TYPE_PUBLIC,
/** Address is either resolvable, non-resolvable or static.*/
ADDRESS_TYPE_RANDOM,
+ /** Address type is unknown or unavailable **/
+ ADDRESS_TYPE_UNKNOWN,
}
)
public @interface AddressType {}
@@ -1140,6 +1142,8 @@
public static final int ADDRESS_TYPE_PUBLIC = 0;
/** Address is either resolvable, non-resolvable or static. */
public static final int ADDRESS_TYPE_RANDOM = 1;
+ /** Address type is unknown or unavailable **/
+ public static final int ADDRESS_TYPE_UNKNOWN = 0xFFFF;
private static final String NULL_MAC_ADDRESS = "00:00:00:00:00:00";
diff --git a/framework/java/android/bluetooth/BluetoothLeAudioCodecConfigMetadata.java b/framework/java/android/bluetooth/BluetoothLeAudioCodecConfigMetadata.java
new file mode 100644
index 0000000..4ca657c
--- /dev/null
+++ b/framework/java/android/bluetooth/BluetoothLeAudioCodecConfigMetadata.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing the codec specific config metadata information defined in the Basic Audio
+ * Profile.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BluetoothLeAudioCodecConfigMetadata implements Parcelable {
+ private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
+
+ private final long mAudioLocation;
+ private final byte[] mRawMetadata;
+
+ private BluetoothLeAudioCodecConfigMetadata(long audioLocation, byte[] rawMetadata) {
+ mAudioLocation = audioLocation;
+ mRawMetadata = rawMetadata;
+ }
+
+ /**
+ * Get the audio location information as defined in the Generic Audio section of Bluetooth
+ * Assigned numbers.
+ *
+ * @return configured audio location, -1 if this metadata does not exist
+ * @hide
+ */
+ @SystemApi
+ public long getAudioLocation() {
+ return mAudioLocation;
+ }
+
+ /**
+ * Get the raw bytes of stream metadata in Bluetooth LTV format.
+ *
+ * Bluetooth LTV format for stream metadata is defined in the Generic Audio
+ * section of <a href="https://www.bluetooth.com/specifications/assigned-numbers/">Bluetooth Assigned Numbers</a>,
+ * including metadata that was not covered by the getter methods in this class.
+ *
+ * @return raw bytes of stream metadata in Bluetooth LTV format
+ * @hide
+ */
+ @SystemApi
+ public @NonNull byte[] getRawMetadata() {
+ return mRawMetadata;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mAudioLocation);
+ if (mRawMetadata != null) {
+ out.writeInt(mRawMetadata.length);
+ out.writeByteArray(mRawMetadata);
+ } else {
+ out.writeInt(-1);
+ }
+ }
+
+ /**
+ * A {@link Parcelable.Creator} to create {@link BluetoothLeAudioCodecConfigMetadata} from
+ * parcel.
+ * @hide
+ */
+ @SystemApi
+ public static final @NonNull Parcelable.Creator<BluetoothLeAudioCodecConfigMetadata> CREATOR =
+ new Parcelable.Creator<BluetoothLeAudioCodecConfigMetadata>() {
+ @NonNull
+ public BluetoothLeAudioCodecConfigMetadata createFromParcel(@NonNull Parcel in) {
+ long audioLocation = in.readLong();
+ int rawMetadataLen = in.readInt();
+ byte[] rawMetadata;
+ if (rawMetadataLen != -1) {
+ rawMetadata = new byte[rawMetadataLen];
+ in.readByteArray(rawMetadata);
+ } else {
+ rawMetadata = new byte[0];
+ }
+ return new BluetoothLeAudioCodecConfigMetadata(audioLocation, rawMetadata);
+ }
+
+ public @NonNull BluetoothLeAudioCodecConfigMetadata[] newArray(int size) {
+ return new BluetoothLeAudioCodecConfigMetadata[size];
+ }
+ };
+
+ /**
+ * Construct a {@link BluetoothLeAudioCodecConfigMetadata} from raw bytes.
+ *
+ * The byte array will be parsed and values for each getter will be populated
+ *
+ * Raw metadata cannot be set using builder in order to maintain raw bytes and getter value
+ * consistency
+ *
+ * @param rawBytes raw bytes of stream metadata in Bluetooth LTV format
+ * @return parsed {@link BluetoothLeAudioCodecConfigMetadata} object
+ * @throws IllegalArgumentException if <var>rawBytes</var> is null or when the raw bytes cannot
+ * be parsed to build the object
+ * @hide
+ */
+ @SystemApi
+ public static @NonNull BluetoothLeAudioCodecConfigMetadata fromRawBytes(
+ @NonNull byte[] rawBytes) {
+ if (rawBytes == null) {
+ throw new IllegalArgumentException("Raw bytes cannot be null");
+ }
+ return null;
+ }
+
+ /**
+ * Builder for {@link BluetoothLeAudioCodecConfigMetadata}.
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private long mAudioLocation = UNKNOWN_VALUE_PLACEHOLDER;
+ private byte[] mRawMetadata = null;
+
+ /**
+ * Create an empty builder.
+ * @hide
+ */
+ @SystemApi
+ public Builder() {}
+
+ /**
+ * Create a builder with copies of information from original object.
+ *
+ * @param original original object
+ * @hide
+ */
+ @SystemApi
+ public Builder(@NonNull BluetoothLeAudioCodecConfigMetadata original) {
+ mAudioLocation = original.getAudioLocation();
+ mRawMetadata = original.getRawMetadata();
+ }
+
+ /**
+ * Set the audio location information as defined in the Generic Audio section of Bluetooth
+ * Assigned numbers.
+ *
+ * @param audioLocation configured audio location, -1 if does not exist
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setAudioLocation(long audioLocation) {
+ mAudioLocation = audioLocation;
+ return this;
+ }
+
+ /**
+ * Build {@link BluetoothLeAudioCodecConfigMetadata}.
+ *
+ * @return constructed {@link BluetoothLeAudioCodecConfigMetadata}
+ * @throws IllegalArgumentException if the object cannot be built
+ * @hide
+ */
+ @SystemApi
+ public @NonNull BluetoothLeAudioCodecConfigMetadata build() {
+ if (mRawMetadata == null) {
+ mRawMetadata = new byte[0];
+ }
+ return new BluetoothLeAudioCodecConfigMetadata(mAudioLocation, mRawMetadata);
+ }
+ }
+}
diff --git a/framework/java/android/bluetooth/BluetoothLeAudioContentMetadata.java b/framework/java/android/bluetooth/BluetoothLeAudioContentMetadata.java
new file mode 100644
index 0000000..47ab698
--- /dev/null
+++ b/framework/java/android/bluetooth/BluetoothLeAudioContentMetadata.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing the media metadata information defined in the Basic Audio Profile.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BluetoothLeAudioContentMetadata implements Parcelable {
+ private final String mProgramInfo;
+ private final String mLanguage;
+ private final byte[] mRawMetadata;
+
+ private BluetoothLeAudioContentMetadata(String programInfo, String language,
+ byte[] rawMetadata) {
+ mProgramInfo = programInfo;
+ mLanguage = language;
+ mRawMetadata = rawMetadata;
+ }
+
+ /**
+ * Get the title and/or summary of Audio Stream content in UTF-8 format.
+ *
+ * @return title and/or summary of Audio Stream content in UTF-8 format, null if this metadata
+ * does not exist
+ * @hide
+ */
+ @SystemApi
+ public @Nullable String getProgramInfo() {
+ return mProgramInfo;
+ }
+
+ /**
+ * Get language of the audio stream in 3-byte, lower case language code as defined in ISO 639-3.
+ *
+ * @return ISO 639-3 formatted language code, null if this metadata does not exist
+ * @hide
+ */
+ @SystemApi
+ public @Nullable String getLanguage() {
+ return mLanguage;
+ }
+
+ /**
+ * Get the raw bytes of stream metadata in Bluetooth LTV format as defined in the Generic Audio
+ * section of <a href="https://www.bluetooth.com/specifications/assigned-numbers/">Bluetooth Assigned Numbers</a>,
+ * including metadata that was not covered by the getter methods in this class
+ *
+ * @return raw bytes of stream metadata in Bluetooth LTV format
+ */
+ public @NonNull byte[] getRawMetadata() {
+ return mRawMetadata;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mProgramInfo);
+ out.writeString(mLanguage);
+ out.writeInt(mRawMetadata.length);
+ out.writeByteArray(mRawMetadata);
+ }
+
+ /**
+ * A {@link Parcelable.Creator} to create {@link BluetoothLeAudioContentMetadata} from parcel.
+ * @hide
+ */
+ @SystemApi
+ public static final @NonNull Parcelable.Creator<BluetoothLeAudioContentMetadata> CREATOR =
+ new Parcelable.Creator<BluetoothLeAudioContentMetadata>() {
+ public @NonNull BluetoothLeAudioContentMetadata createFromParcel(
+ @NonNull Parcel in) {
+ final String programInfo = in.readString();
+ final String language = in.readString();
+ final int rawMetadataLength = in.readInt();
+ byte[] rawMetadata = new byte[rawMetadataLength];
+ in.readByteArray(rawMetadata);
+ return new BluetoothLeAudioContentMetadata(programInfo, language, rawMetadata);
+ }
+
+ public @NonNull BluetoothLeAudioContentMetadata[] newArray(int size) {
+ return new BluetoothLeAudioContentMetadata[size];
+ }
+ };
+
+ /**
+ * Construct a {@link BluetoothLeAudioContentMetadata} from raw bytes.
+ *
+ * The byte array will be parsed and values for each getter will be populated
+ *
+ * Raw metadata cannot be set using builder in order to maintain raw bytes and getter value
+ * consistency
+ *
+ * @param rawBytes raw bytes of stream metadata in Bluetooth LTV format
+ * @return parsed {@link BluetoothLeAudioContentMetadata} object
+ * @throws IllegalArgumentException if <var>rawBytes</var> is null or when the raw bytes cannot
+ * be parsed to build the object
+ * @hide
+ */
+ @SystemApi
+ public static @NonNull BluetoothLeAudioContentMetadata fromRawBytes(@NonNull byte[] rawBytes) {
+ if (rawBytes == null) {
+ throw new IllegalArgumentException("Raw bytes cannot be null");
+ }
+ return null;
+ }
+
+ /**
+ * Builder for {@link BluetoothLeAudioContentMetadata}.
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private String mProgramInfo = null;
+ private String mLanguage = null;
+ private byte[] mRawMetadata = null;
+
+ /**
+ * Create an empty builder
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder() {}
+
+ /**
+ * Create a builder with copies of information from original object.
+ *
+ * @param original original object
+ * @hide
+ */
+ @SystemApi
+ public Builder(@NonNull BluetoothLeAudioContentMetadata original) {
+ mProgramInfo = original.getProgramInfo();
+ mLanguage = original.getLanguage();
+ mRawMetadata = original.getRawMetadata();
+ }
+
+ /**
+ * Set the title and/or summary of Audio Stream content in UTF-8 format.
+ *
+ * @param programInfo title and/or summary of Audio Stream content in UTF-8 format, null
+ * if this metadata does not exist
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setProgramInfo(@Nullable String programInfo) {
+ mProgramInfo = programInfo;
+ return this;
+ }
+
+ /**
+ * Set language of the audio stream in 3-byte, lower case language code as defined in
+ * ISO 639-3.
+ *
+ * @return ISO 639-3 formatted language code, null if this metadata does not exist
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setLanguage(@Nullable String language) {
+ mLanguage = language;
+ return this;
+ }
+
+ /**
+ * Build {@link BluetoothLeAudioContentMetadata}.
+ *
+ * @return constructed {@link BluetoothLeAudioContentMetadata}
+ * @throws IllegalArgumentException if the object cannot be built
+ * @hide
+ */
+ @SystemApi
+ public @NonNull BluetoothLeAudioContentMetadata build() {
+ if (mRawMetadata == null) {
+ mRawMetadata = new byte[0];
+ }
+ return new BluetoothLeAudioContentMetadata(mProgramInfo, mLanguage, mRawMetadata);
+ }
+ }
+}
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcast.java b/framework/java/android/bluetooth/BluetoothLeBroadcast.java
index fed9f91..4537cc6 100644
--- a/framework/java/android/bluetooth/BluetoothLeBroadcast.java
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcast.java
@@ -16,270 +16,412 @@
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
- *
+ * Interface for receiving events related to Broadcast Source
* @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
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_STATE_DISABLED = 10;
-
- /**
- * Indicates that LE Audio Broadcast mode is being enabled
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_STATE_ENABLING = 11;
-
- /**
- * 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;
-
- /**
- * Indicates that an LE Audio Broadcast mode is currently playing
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_STATE_PLAYING = 14;
-
- /**
- * Indicates that LE Audio Broadcast is currently not playing
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_STATE_NOT_PLAYING = 15;
-
- /**
- * 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 {}
-
- /**
- * Indicates that the LE Audio Broadcast encryption key size is 32 bits.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT = 16;
-
- /**
- * Indicates that the LE Audio Broadcast encryption key size is 128 bits.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT = 17;
-
- /**
- * Interface for receiving events related to broadcasts
- */
+ @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 {}
+
/**
- * Called when broadcast state has changed
+ * Callback invoked when broadcast is started, but audio may not be playing.
*
- * @param prevState broadcast state before the change
- * @param newState broadcast state after the change
+ * 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
*/
- @LeAudioBroadcastState
- void onBroadcastStateChange(int prevState, int newState);
+ @SystemApi
+ void onBroadcastStarted(@Reason int reason, int broadcastId);
+
/**
- * Called when encryption key has been updated
+ * Callback invoked when broadcast failed to start
*
- * @param success true if the key was updated successfully, false otherwise
+ * @param reason for broadcast start failure
+ * @hide
*/
- void onEncryptionKeySet(boolean success);
+ @SystemApi
+ void onBroadcastStartFailed(@Reason int reason);
+
+ /**
+ * Callback invoked when broadcast is stopped
+ *
+ * @param reason for broadcast stop
+ * @hide
+ */
+ @SystemApi
+ void onBroadcastStopped(@Reason int reason, int broadcastId);
+
+ /**
+ * Callback invoked when broadcast failed to stop
+ *
+ * @param reason for broadcast stop failure
+ * @hide
+ */
+ @SystemApi
+ void onBroadcastStopFailed(@Reason int reason);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * Callback invoked when Broadcast Source failed to update
+ *
+ * @param reason for update failure
+ * @param broadcastId as defined by the Basic Audio Profile
+ * @hide
+ */
+ @SystemApi
+ void onBroadcastUpdateFailed(int reason, int broadcastId);
+
+ /**
+ * Callback invoked when Broadcast Source metadata is updated
+ *
+ * @param metadata updated Broadcast Source metadata
+ * @param broadcastId as defined by the Basic Audio Profile
+ * @hide
+ */
+ @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() {
+ @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");
+ }
+
+ /**
+ * 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
+ */
+ @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");
+ }
+
+ /**
+ * Update the broadcast with <var>broadcastId</var> with new <var>contentMetadata</var>
+ *
+ * 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
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void updateBroadcast(int broadcastId,
+ @NonNull BluetoothLeAudioContentMetadata contentMetadata) {
+
+ }
+
+ /**
+ * Stop broadcasting.
+ *
+ * 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
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void stopBroadcast(int broadcastId) {
if (DBG) log("disableBroadcastMode");
- return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED;
}
/**
- * Get the current LE Audio broadcast state
+ * 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
*/
- @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 boolean isPlaying(int broadcastId) {
+ return false;
}
/**
- * Enable LE Audio broadcast encryption
+ * Get {@link BluetoothLeBroadcastMetadata} for all Broadcast Groups currently running on
+ * this device
*
- * @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
- *
+ * @return list of {@link BluetoothLeBroadcastMetadata}
* @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 @NonNull List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
+ return Collections.emptyList();
}
/**
- * Disable LE Audio broadcast encryption
- *
- * @param removeExisting true, if the existing key should be removed
- * false, otherwise
- *
+ * Get the maximum number of broadcast groups supported on this device
+ * @return maximum number of broadcast groups supported on this device
* @hide
*/
- public int disableEncryption(boolean removeExisting) {
- if (DBG) log("disableEncryption removeExisting=" + removeExisting);
- return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED;
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public int getMaximumNumberOfBroadcast() {
+ return 1;
}
/**
- * 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)
- *
+ * {@inheritDoc}
* @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;
- }
-
-
- /**
- * Get the encryption key that was set before
- *
- * @return encryption key as a byte array or null if no encryption key was set
- *
- * @hide
- */
- public byte[] getEncryptionKey() {
- if (DBG) log("getEncryptionKey");
- return null;
- }
+ @Override
+ public void close() throws Exception {}
private static void log(String msg) {
Log.d(TAG, msg);
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
new file mode 100644
index 0000000..81f5d18
--- /dev/null
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
@@ -0,0 +1,738 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
+import android.bluetooth.annotations.RequiresBluetoothScanPermission;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanSettings;
+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 for the LE Audio Broadcast Assistant role, which implements
+ * client side control points for Broadcast Audio Scan Service (BASS).
+ *
+ * <p>An LE Audio Broadcast Assistant can help a Broadcast Sink to scan for available Broadcast
+ * Sources. The Broadcast Sink achieves this by offloading the scan to a Broadcast Assistant.
+ * This is facilitated by the Broadcast Audio Scan Service (BASS). A BASS server is a GATT
+ * server that is part of the Scan Delegator on a Broadcast Sink. A BASS client instead runs on
+ * the Broadcast Assistant.
+ *
+ * <p>Once a GATT connection is established between the BASS client and the BASS server, the
+ * Broadcast Sink can offload the scans to the Broadcast Assistant. Upon finding new Broadcast
+ * Sources, the Broadcast Assistant then notifies the Broadcast Sink about these over the
+ * established GATT connection. The Scan Delegator on the Broadcast Sink can also notify the
+ * Assistant about changes such as addition and removal of Broadcast Sources.
+ *
+ * In the context of this class, BASS server will be addressed as Broadcast Sink and BASS client
+ * will be addressed as Broadcast Assistant.
+ *
+ * <p>BluetoothLeBroadcastAssistant is a proxy object for controlling the Broadcast Assistant
+ * service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
+ * BluetoothLeBroadcastAssistant proxy object.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BluetoothLeBroadcastAssistant implements BluetoothProfile {
+ private static final String TAG = "BluetoothLeBroadcastAssistant";
+ private static final boolean DBG = true;
+
+ /**
+ * This class provides a set of callbacks that are invoked when scanning for Broadcast Sources
+ * is offloaded to a Broadcast Assistant.
+ *
+ * @hide
+ */
+ @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_REMOTE_REQUEST,
+ BluetoothStatusCodes.REASON_SYSTEM_POLICY,
+ BluetoothStatusCodes.ERROR_HARDWARE_GENERIC,
+ BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_DUPLICATE_ADDITION,
+ BluetoothStatusCodes.ERROR_BAD_PARAMETERS,
+ BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR,
+ BluetoothStatusCodes.ERROR_REMOTE_NOT_ENOUGH_RESOURCES,
+ BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID,
+ BluetoothStatusCodes.ERROR_ALREADY_IN_TARGET_STATE,
+ BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED,
+ })
+ @interface Reason {}
+
+ /**
+ * Callback invoked when the implementation started searching for nearby Broadcast Sources.
+ *
+ * @param reason reason code on why search has started
+ * @hide
+ */
+ @SystemApi
+ void onSearchStarted(@Reason int reason);
+
+ /**
+ * Callback invoked when the implementation failed to start searching for nearby broadcast
+ * sources.
+ *
+ * @param reason reason for why search failed to start
+ * @hide
+ */
+ @SystemApi
+ void onSearchStartFailed(@Reason int reason);
+
+ /**
+ * Callback invoked when the implementation stopped searching for nearby Broadcast Sources.
+ *
+ * @param reason reason code on why search has stopped
+ * @hide
+ */
+ @SystemApi
+ void onSearchStopped(@Reason int reason);
+
+ /**
+ * Callback invoked when the implementation failed to stop searching for nearby broadcast
+ * sources.
+ *
+ * @param reason for why search failed to start
+ * @hide
+ */
+ @SystemApi
+ void onSearchStopFailed(@Reason int reason);
+
+ /**
+ * Callback invoked when a new Broadcast Source is found together with the
+ * {@link BluetoothLeBroadcastMetadata}.
+ *
+ * @param source {@link BluetoothLeBroadcastMetadata} representing a Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source);
+
+ /**
+ * Callback invoked when a new Broadcast Source has been successfully added to the
+ * Broadcast Sink.
+ *
+ * Broadcast audio stream may not have been started after this callback, the caller need
+ * to monitor
+ * {@link #onReceiveStateChanged(BluetoothDevice, int, BluetoothLeBroadcastReceiveState)}
+ * to see if synchronization with Broadcast Source is successful
+ *
+ * When <var>isGroupOp</var> is true when
+ * {@link #addSource(BluetoothDevice, BluetoothLeBroadcastMetadata, boolean)}
+ * is called, each Broadcast Sink device in the coordinated set will trigger and individual
+ * update
+ *
+ * A new source could be added by the Broadcast Sink itself or other Broadcast Assistants
+ * connected to the Broadcast Sink and in this case the reason code will be
+ * {@link BluetoothStatusCodes#REASON_REMOTE_REQUEST}
+ *
+ * @param sink Broadcast Sink device on which a new Broadcast Source has been added
+ * @param sourceId source ID as defined in the BASS specification
+ * @param reason reason of source addition
+ * @hide
+ */
+ @SystemApi
+ void onSourceAdded(@NonNull BluetoothDevice sink, @Reason int sourceId,
+ @Reason int reason);
+
+ /**
+ * Callback invoked when the new Broadcast Source failed to be added to the Broadcast Sink.
+ *
+ * @param sink Broadcast Sink device on which a new Broadcast Source has been added
+ * @param source metadata representation of the Broadcast Source
+ * @param reason reason why the addition has failed
+ * @hide
+ */
+ @SystemApi
+ void onSourceAddFailed(@NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastMetadata source, @Reason int reason);
+
+ /**
+ * Callback invoked when an existing Broadcast Source within a Broadcast Sink has been
+ * modified.
+ *
+ * Actual state after the modification will be delivered via the next
+ * {@link Callback#onReceiveStateChanged(BluetoothDevice, int,
+ * BluetoothLeBroadcastReceiveState)}
+ * callback.
+ *
+ * A source could be modified by the Broadcast Sink itself or other Broadcast Assistants
+ * connected to the Broadcast Sink and in this case the reason code will be
+ * {@link BluetoothStatusCodes#REASON_REMOTE_REQUEST}
+ *
+ * @param sink Broadcast Sink device on which a Broadcast Source has been modified
+ * @param sourceId source ID as defined in the BASS specification
+ * @param reason reason of source modification
+ * @hide
+ */
+ @SystemApi
+ void onSourceModified(@NonNull BluetoothDevice sink, int sourceId, @Reason int reason);
+
+ /**
+ * Callback invoked when the Broadcast Assistant failed to modify an existing Broadcast
+ * Source on a Broadcast Sink.
+ *
+ * @param sink Broadcast Sink device on which a Broadcast Source has been modified
+ * @param sourceId source ID as defined in the BASS specification
+ * @param reason reason why the modification has failed
+ * @hide
+ */
+ @SystemApi
+ void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId, @Reason int reason);
+
+ /**
+ * Callback invoked when a Broadcast Source has been successfully removed from the
+ * Broadcast Sink.
+ *
+ * No more update for the source ID via
+ * {@link Callback#onReceiveStateChanged(BluetoothDevice, int,
+ * BluetoothLeBroadcastReceiveState)}
+ * after this callback.
+ *
+ * A source could be removed by the Broadcast Sink itself or other Broadcast Assistants
+ * connected to the Broadcast Sink and in this case the reason code will be
+ * {@link BluetoothStatusCodes#REASON_REMOTE_REQUEST}
+ *
+ * @param sink Broadcast Sink device from which a Broadcast Source has been removed
+ * @param sourceId source ID as defined in the BASS specification
+ * @param reason reason why the Broadcast Source was removed
+ * @hide
+ */
+ @SystemApi
+ void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId, @Reason int reason);
+
+ /**
+ * Callback invoked when the Broadcast Assistant failed to remove an existing Broadcast
+ * Source on a Broadcast Sink.
+ *
+ * @param sink Broadcast Sink device on which a Broadcast Source was to be removed
+ * @param sourceId source ID as defined in the BASS specification
+ * @param reason reason why the modification has failed
+ * @hide
+ */
+ @SystemApi
+ void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId, @Reason int reason);
+
+ /**
+ * Callback invoked when the Broadcast Receive State information of a Broadcast Sink device
+ * changes.
+ *
+ * @param sink BASS server device that is also a Broadcast Sink device
+ * @param sourceId source ID as defined in the BASS specification
+ * @param state latest state information between the Broadcast Sink and a Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId,
+ @NonNull BluetoothLeBroadcastReceiveState state);
+ }
+
+ /**
+ * Intent used to broadcast the change in connection state of devices via Broadcast Audio Scan
+ * Service (BASS). Please note that in a coordinated set, each set member will connect via BASS
+ * individually. Group operations on a single set member will propagate to the entire set.
+ *
+ * For example, in the binaural case, there will be two different LE devices for the left and
+ * right side and each device will have their own connection state changes. If both devices
+ * belongs to on Coordinated Set, group operation on one of them will affect both devices.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.action.CONNECTION_STATE_CHANGED";
+
+ /**
+ * Create a new instance of an LE Audio Broadcast Assistant.
+ *
+ * @hide
+ */
+ /*package*/ BluetoothLeBroadcastAssistant(
+ @NonNull Context context, @NonNull ServiceListener listener) {
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ @Override
+ public @BluetoothProfile.BtProfileState int getConnectionState(@NonNull BluetoothDevice sink) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ @Override
+ public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ @NonNull int[] states) {
+ return Collections.emptyList();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ @Override
+ @NonNull
+ public List<BluetoothDevice> getConnectedDevices() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Set connection policy of the profile.
+ *
+ * <p> The device should already be paired. Connection policy can be one of {
+ * @link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
+ * {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Paired bluetooth device
+ * @param connectionPolicy is the connection policy to set to for this profile
+ * @return true if connectionPolicy is set, false on error
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
+ return false;
+ }
+
+ /**
+ * Get the connection policy of the profile.
+ *
+ * <p> The connection policy can be any of:
+ * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
+ * {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Bluetooth device
+ * @return connection policy of the device
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+
+ /**
+ * Register a {@link Callback} that will be invoked during the operation of this profile.
+ *
+ * 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. 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
+ */
+ @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");
+ }
+
+ /**
+ * 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
+ */
+ @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");
+ }
+
+ /**
+ * Search for LE Audio Broadcast Sources on behalf of all devices connected via Broadcast Audio
+ * Scan Service, filtered by <var>filters</var>.
+ *
+ * On success, {@link Callback#onSearchStarted(int)} will be called with reason code
+ * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}.
+ *
+ * On failure, {@link Callback#onSearchStartFailed(int)} will be called with reason code
+ *
+ * The implementation will also synchronize with discovered Broadcast Sources and get their
+ * metadata before passing the Broadcast Source metadata back to the application using {@link
+ * Callback#onSourceFound(BluetoothLeBroadcastMetadata)}.
+ *
+ * Please disconnect the Broadcast Sink's BASS server by calling
+ * {@link #setConnectionPolicy(BluetoothDevice, int)} with
+ * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} if you do not want the Broadcast Sink
+ * to receive notifications about this search before calling this method.
+ *
+ * App must also have
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}
+ * permission in order to get results.
+ *
+ * <var>filters</var> will be AND'ed with internal filters in the implementation and
+ * {@link ScanSettings} will be managed by the implementation.
+ *
+ * @param filters {@link ScanFilter}s for finding exact Broadcast Source, if no filter is
+ * needed, please provide an empty list instead
+ * @throws IllegalArgumentException when <var>filters</var> argument is null
+ * @throws IllegalStateException when no callback is registered
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_SCAN,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void startSearchingForSources(@NonNull List<ScanFilter> filters) {
+ log("searchForBroadcastSources");
+ if (filters == null) {
+ throw new IllegalArgumentException("filters can be empty, but not null");
+ }
+ throw new UnsupportedOperationException("Not Implemented");
+ }
+
+ /**
+ * Stops an ongoing search for nearby Broadcast Sources.
+ *
+ * On success, {@link Callback#onSearchStopped(int)} will be called with reason code
+ * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}.
+ * On failure, {@link Callback#onSearchStopFailed(int)} will be called with reason code
+ *
+ * @throws IllegalStateException if callback was not registered
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void stopSearchingForSources() {
+ log("stopSearchingForSources:");
+ throw new UnsupportedOperationException("Not Implemented");
+ }
+
+ /**
+ * Return true if a search has been started by this application.
+ *
+ * @return true if a search has been started by this application
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public boolean isSearchInProgress() {
+ return false;
+ }
+
+ /**
+ * Add a Broadcast Source to the Broadcast Sink.
+ *
+ * Caller can modify <var>sourceMetadata</var> before using it in this method to set a
+ * Broadcast Code, to select a different Broadcast Channel in a Broadcast Source such as channel
+ * with a different language, and so on. What can be modified is listed in the documentation of
+ * {@link #modifySource(BluetoothDevice, int, BluetoothLeBroadcastMetadata)} and can also be
+ * modified after a source is added.
+ *
+ * On success, {@link Callback#onSourceAdded(BluetoothDevice, int, int)} will be invoked with
+ * a <var>sourceID</var> assigned by the Broadcast Sink with reason code
+ * {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}. However, this callback only indicates
+ * that the Broadcast Sink has allocated resource to receive audio from the Broadcast Source,
+ * and audio stream may not have started. The caller should then wait for
+ * {@link Callback#onReceiveStateChanged(BluetoothDevice, int,
+ * BluetoothLeBroadcastReceiveState)}
+ * callback to monitor the encryption and audio sync state.
+ *
+ * Note that wrong broadcast code will not prevent the source from being added to the Broadcast
+ * Sink. Caller should modify the current source to correct the broadcast code.
+ *
+ * On failure,
+ * {@link Callback#onSourceAddFailed(BluetoothDevice, BluetoothLeBroadcastMetadata, int)}
+ * will be invoked with the same <var>source</var> metadata and reason code
+ *
+ * When too many sources was added to Broadcast sink, error
+ * {@link BluetoothStatusCodes#ERROR_REMOTE_NOT_ENOUGH_RESOURCES} will be delivered. In this
+ * case, check the capacity of Broadcast sink via
+ * {@link #getMaximumSourceCapacity(BluetoothDevice)} and the current list of sources via
+ * {@link #getAllSources(BluetoothDevice)}.
+ *
+ * Some sources might be added by other Broadcast Assistants and hence was not
+ * in {@link Callback#onSourceAdded(BluetoothDevice, int, int)} callback, but will be updated
+ * via {@link Callback#onReceiveStateChanged(BluetoothDevice, int,
+ * BluetoothLeBroadcastReceiveState)}
+ *
+ * <p>If there are multiple members in the coordinated set the sink belongs to, and isGroupOp is
+ * set to true, the Broadcast Source will be added to each sink in the coordinated set and a
+ * separate {@link Callback#onSourceAdded} callback will be invoked for each member of the
+ * coordinated set.
+ *
+ * <p>The <var>isGroupOp</var> option is sticky. This means that subsequent operations using
+ * {@link #modifySource(BluetoothDevice, int, BluetoothLeBroadcastMetadata)} and
+ * {@link #removeSource(BluetoothDevice, int)} will act on all devices in the same coordinated
+ * set for the <var>sink</var> and <var>sourceID</var> pair until the <var>sourceId</var> is
+ * removed from the <var>sink</var> by any Broadcast role (could be another remote device).
+ *
+ * <p>When <var>isGroupOp</var> is true, if one Broadcast Sink in a coordinated set
+ * disconnects from this Broadcast Assistant or lost the Broadcast Source, this Broadcast
+ * Assistant will try to add it back automatically to make sure the whole coordinated set
+ * is in the same state.
+ *
+ * @param sink Broadcast Sink to which the Broadcast Source should be added
+ * @param sourceMetadata Broadcast Source metadata to be added to the Broadcast Sink
+ * @param isGroupOp {@code true} if Application wants to perform this operation for all
+ * coordinated set members throughout this session. Otherwise, caller
+ * would have to add, modify, and remove individual set members.
+ * @throws IllegalArgumentException if <var>sink</var> or <var>source</var> are null
+ * @throws IllegalStateException if callback was not registered
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void addSource(@NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastMetadata sourceMetadata, boolean isGroupOp) {
+ log("addBroadcastSource: " + sourceMetadata + " on " + sink);
+ throw new UnsupportedOperationException("Not Implemented");
+ }
+
+ /**
+ * Modify the Broadcast Source information on a Broadcast Sink.
+ *
+ * One can modify {@link BluetoothLeBroadcastMetadata#getBroadcastCode()} if
+ * {@link BluetoothLeBroadcastReceiveState#getBigEncryptionState()} returns
+ * {@link BluetoothLeBroadcastReceiveState#BIG_ENCRYPTION_STATE_BAD_CODE} or
+ * {@link BluetoothLeBroadcastReceiveState#BIG_ENCRYPTION_STATE_CODE_REQUIRED}
+ *
+ * One can modify {@link BluetoothLeBroadcastMetadata#getPaSyncInterval()} if the Broadcast
+ * Assistant received updated information.
+ *
+ * One can modify {@link BluetoothLeBroadcastChannel#isSelected()} to select different broadcast
+ * channel to listen to (one per {@link BluetoothLeBroadcastSubgroup} or set
+ * {@link BluetoothLeBroadcastSubgroup#isNoChannelPreference()} to leave the choice to the
+ * Broadcast Sink.
+ *
+ * One can modify {@link BluetoothLeBroadcastSubgroup#getContentMetadata()} if the subgroup
+ * metadata changes and the Broadcast Sink need help updating the metadata from Broadcast
+ * Assistant.
+ *
+ * Each of the above modifications can be accepted or rejected by the Broadcast Assistant
+ * implement and/or the Broadcast Sink.
+ *
+ * <p>On success, {@link Callback#onSourceModified(BluetoothDevice, int, int)} will be invoked
+ * with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}.
+ *
+ * <p>On failure, {@link Callback#onSourceModifyFailed(BluetoothDevice, int, int)} will be
+ * invoked with reason code.
+ *
+ * <p>If there are multiple members in the coordinated set the sink belongs to, and isGroupOp
+ * is set to true during
+ * {@link #addSource(BluetoothDevice, BluetoothLeBroadcastMetadata, boolean)},
+ * the source will be modified on each sink in the coordinated set and a separate
+ * {@link Callback#onSourceModified(BluetoothDevice, int, int)} callback will be invoked for
+ * each member of the coordinated set.
+ *
+ * @param sink Broadcast Sink to which the Broadcast Source should be updated
+ * @param sourceId source ID as delivered in
+ * {@link Callback#onSourceAdded(BluetoothDevice, int, int)}
+ * @param updatedMetadata updated Broadcast Source metadata to be updated on the Broadcast Sink
+ * @throws IllegalStateException if callback was not registered
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void modifySource(@NonNull BluetoothDevice sink, int sourceId,
+ @NonNull BluetoothLeBroadcastMetadata updatedMetadata) {
+ log("updateBroadcastSource: " + updatedMetadata + " on " + sink);
+ throw new UnsupportedOperationException("Not Implemented");
+ }
+
+ /**
+ * Removes the Broadcast Source from a Broadcast Sink.
+ *
+ * <p>On success, {@link Callback#onSourceRemoved(BluetoothDevice, int, int)} will be invoked
+ * with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}.
+ *
+ * <p>On failure, {@link Callback#onSourceRemoveFailed(BluetoothDevice, int, int)} will be
+ * invoked with reason code.
+
+ *
+ * <p>If there are multiple members in the coordinated set the sink belongs to, and isGroupOp is
+ * set to true during
+ * {@link #addSource(BluetoothDevice, BluetoothLeBroadcastMetadata, boolean)},
+ * the source will be removed from each sink in the coordinated set and a separate
+ * {@link Callback#onSourceRemoved(BluetoothDevice, int, int)} callback will be invoked for
+ * each member of the coordinated set.
+ *
+ * @param sink Broadcast Sink from which a Broadcast Source should be removed
+ * @param sourceId source ID as delivered in
+ * {@link Callback#onSourceAdded(BluetoothDevice, int, int)}
+ * @throws IllegalArgumentException when the <var>sink</var> is null
+ * @throws IllegalStateException if callback was not registered
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void removeSource(@NonNull BluetoothDevice sink, int sourceId) {
+ log("removeBroadcastSource: " + sourceId + " from " + sink);
+ return;
+ }
+
+
+ /**
+ * Get information about all Broadcast Sources that a Broadcast Sink knows about.
+ *
+ * @param sink Broadcast Sink from which to get all Broadcast Sources
+ * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState}
+ * stored in the Broadcast Sink
+ * @throws IllegalArgumentException when <var>sink</var> is null
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @NonNull List<BluetoothLeBroadcastReceiveState> getAllSources(
+ @NonNull BluetoothDevice sink) {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Get maximum number of sources that can be added to this Broadcast Sink.
+ *
+ * @param sink Broadcast Sink device
+ * @return maximum number of sources that can be added to this Broadcast Sink
+ * @throws IllegalArgumentException when <var>sink</var> is null
+ * @hide
+ */
+ @SystemApi
+ public int getMaximumSourceCapacity(@NonNull BluetoothDevice sink) {
+ return 0;
+ }
+
+ private static void log(@NonNull String msg) {
+ if (DBG) {
+ Log.d(TAG, msg);
+ }
+ }
+}
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
deleted file mode 100644
index c6d161e..0000000
--- a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.bluetooth.le.ScanResult;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * This class provides a set of callbacks that are invoked when scanning for Broadcast Sources is
- * offloaded to a Broadcast Assistant.
- *
- * <p>An LE Audio Broadcast Assistant can help a Broadcast Sink to scan for available Broadcast
- * Sources. The Broadcast Sink achieves this by offloading the scan to a Broadcast Assistant. This
- * is facilitated by the Broadcast Audio Scan Service (BASS). A BASS server is a GATT server that is
- * part of the Scan Delegator on a Broadcast Sink. A BASS client instead runs on the Broadcast
- * Assistant.
- *
- * <p>Once a GATT connection is established between the BASS client and the BASS server, the
- * Broadcast Sink can offload the scans to the Broadcast Assistant. Upon finding new Broadcast
- * Sources, the Broadcast Assistant then notifies the Broadcast Sink about these over the
- * established GATT connection. The Scan Delegator on the Broadcast Sink can also notify the
- * Assistant about changes such as addition and removal of Broadcast Sources.
- *
- * @hide
- */
-public abstract class BluetoothLeBroadcastAssistantCallback {
-
- /**
- * Broadcast Audio Scan Service (BASS) codes returned by a BASS Server
- *
- * @hide
- */
- @IntDef(
- prefix = "BASS_STATUS_",
- value = {
- BASS_STATUS_SUCCESS,
- BASS_STATUS_FAILURE,
- BASS_STATUS_INVALID_GATT_HANDLE,
- BASS_STATUS_TXN_TIMEOUT,
- BASS_STATUS_INVALID_SOURCE_ID,
- BASS_STATUS_COLOCATED_SRC_UNAVAILABLE,
- BASS_STATUS_INVALID_SOURCE_SELECTED,
- BASS_STATUS_SOURCE_UNAVAILABLE,
- BASS_STATUS_DUPLICATE_ADDITION,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface BassStatus {}
-
- public static final int BASS_STATUS_SUCCESS = 0x00;
- public static final int BASS_STATUS_FAILURE = 0x01;
- public static final int BASS_STATUS_INVALID_GATT_HANDLE = 0x02;
- public static final int BASS_STATUS_TXN_TIMEOUT = 0x03;
-
- public static final int BASS_STATUS_INVALID_SOURCE_ID = 0x04;
- public static final int BASS_STATUS_COLOCATED_SRC_UNAVAILABLE = 0x05;
- public static final int BASS_STATUS_INVALID_SOURCE_SELECTED = 0x06;
- public static final int BASS_STATUS_SOURCE_UNAVAILABLE = 0x07;
- public static final int BASS_STATUS_DUPLICATE_ADDITION = 0x08;
- public static final int BASS_STATUS_NO_EMPTY_SLOT = 0x09;
- public static final int BASS_STATUS_INVALID_GROUP_OP = 0x10;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.STATE_CONNECTING,
- BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.STATE_DISCONNECTING
- })
- public @interface ConnectionStateValues {}
-
- /**
- * Callback invoked when the connection state for an LE Audio Broadcast Sink changes
- */
- public void onConnectionStateChange(@ConnectionStateValues int prevState,
- @ConnectionStateValues int newState) {}
-
- /**
- * Callback invoked when a new LE Audio Broadcast Source is found.
- *
- * @param result {@link ScanResult} scan result representing a Broadcast Source
- */
- public void onSourceFound(@NonNull ScanResult result) {}
-
- /**
- * Callback invoked when the Broadcast Assistant synchronizes with Periodic Advertisements (PAs)
- * of an LE Audio Broadcast Source.
- *
- * @param source the selected Broadcast Source
- */
- public void onSourceSelected(
- @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
-
- /**
- * Callback invoked when the Broadcast Assistant loses synchronization with an LE Audio
- * Broadcast Source.
- *
- * @param source the Broadcast Source with which synchronization was lost
- */
- public void onSourceLost(
- @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
-
- /**
- * Callback invoked when a new LE Audio Broadcast Source has been successfully added to the Scan
- * Delegator (within a Broadcast Sink, for example).
- *
- * @param sink Scan Delegator device on which a new Broadcast Source has been added
- * @param source the added Broadcast Source
- */
- public void onSourceAdded(
- @NonNull BluetoothDevice sink,
- @NonNull BluetoothLeBroadcastSourceInfo source,
- @BassStatus int status) {}
-
- /**
- * Callback invoked when an existing LE Audio Broadcast Source within a remote Scan Delegator
- * has been updated.
- *
- * @param sink Scan Delegator device on which a Broadcast Source has been updated
- * @param source the updated Broadcast Source
- */
- public void onSourceUpdated(
- @NonNull BluetoothDevice sink,
- @NonNull BluetoothLeBroadcastSourceInfo source,
- @BassStatus int status) {}
-
- /**
- * Callback invoked when an LE Audio Broadcast Source has been successfully removed from the
- * Scan Delegator (within a Broadcast Sink, for example).
- *
- * @param sink Scan Delegator device from which a Broadcast Source has been removed
- * @param source the removed Broadcast Source
- */
- public void onSourceRemoved(
- @NonNull BluetoothDevice sink,
- @NonNull BluetoothLeBroadcastSourceInfo source,
- @BassStatus int status) {}
-}
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java b/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java
new file mode 100644
index 0000000..6addc06
--- /dev/null
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains the Broadcast Isochronous Channel level information as defined in the BASE
+ * structure of the Basic Audio Profile.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BluetoothLeBroadcastChannel implements Parcelable {
+ private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
+
+ private final boolean mIsSelected;
+ private final int mChannelIndex;
+ private final BluetoothLeAudioCodecConfigMetadata mCodecMetadata;
+
+ private BluetoothLeBroadcastChannel(boolean isSelected, int channelIndex,
+ BluetoothLeAudioCodecConfigMetadata codecMetadata) {
+ mIsSelected = isSelected;
+ mChannelIndex = channelIndex;
+ mCodecMetadata = codecMetadata;
+ }
+
+ /**
+ * Return true if the channel is selected by Broadcast Assistant for the Broadcast Sink.
+ *
+ * Used by Broadcast Assistant and Sink, but not Broadcast Source
+ *
+ * @return true if the channel is selected by Broadcast Assistant for the Broadcast Sink
+ * @hide
+ */
+ @SystemApi
+ public boolean isSelected() {
+ return mIsSelected;
+ }
+
+ /**
+ * Get the Broadcast Isochronous Channel index of this Broadcast Channel.
+ *
+ * @return Broadcast Isochronous Channel index
+ * @hide
+ */
+ @SystemApi
+ public int getChannelIndex() {
+ return mChannelIndex;
+ }
+
+ /**
+ * Return the codec specific configuration for this Broadcast Channel.
+ *
+ * @return codec specific configuration for this Broadcast Channel
+ * @hide
+ */
+ @SystemApi
+ public @NonNull BluetoothLeAudioCodecConfigMetadata getCodecMetadata() {
+ return mCodecMetadata;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeBoolean(mIsSelected);
+ out.writeInt(mChannelIndex);
+ out.writeTypedObject(mCodecMetadata, 0);
+ }
+
+ /**
+ * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastChannel} from parcel.
+ * @hide
+ */
+ @SystemApi
+ public static final @NonNull Parcelable.Creator<BluetoothLeBroadcastChannel> CREATOR =
+ new Parcelable.Creator<BluetoothLeBroadcastChannel>() {
+ public @NonNull BluetoothLeBroadcastChannel createFromParcel(@NonNull Parcel in) {
+ BluetoothLeBroadcastChannel.Builder
+ builder = new BluetoothLeBroadcastChannel.Builder();
+ builder.setSelected(in.readBoolean());
+ builder.setChannelIndex(in.readInt());
+ builder.setCodecMetadata(
+ in.readTypedObject(BluetoothLeAudioCodecConfigMetadata.CREATOR));
+ return builder.build();
+ }
+
+ public @NonNull BluetoothLeBroadcastChannel[] newArray(int size) {
+ return new BluetoothLeBroadcastChannel[size];
+ }
+ };
+
+ /**
+ * Builder for {@link BluetoothLeBroadcastChannel}.
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private boolean mIsSelected = false;
+ private int mChannelIndex = UNKNOWN_VALUE_PLACEHOLDER;
+ private BluetoothLeAudioCodecConfigMetadata mCodecMetadata = null;
+
+ /**
+ * Create an empty builder.
+ * @hide
+ */
+ @SystemApi
+ public Builder() {}
+
+ /**
+ * Create a builder with copies of information from original object.
+ *
+ * @param original original object
+ * @hide
+ */
+ @SystemApi
+ public Builder(@NonNull BluetoothLeBroadcastChannel original) {
+ mIsSelected = original.isSelected();
+ mChannelIndex = original.getChannelIndex();
+ mCodecMetadata = original.getCodecMetadata();
+ }
+
+ /**
+ * Set if the channel is selected by Broadcast Assistant for the Broadcast Sink.
+ *
+ * Used by Broadcast Assistant and Sink, but not Broadcast Source
+ *
+ * @param isSelected true if the channel is selected by Broadcast Assistant for the
+ * Broadcast Sink
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setSelected(boolean isSelected) {
+ mIsSelected = isSelected;
+ return this;
+ }
+
+ /**
+ * Set the Broadcast Isochronous Channel index of this Broadcast Channel.
+ *
+ * @return Broadcast Isochronous Channel index
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setChannelIndex(int channelIndex) {
+ mChannelIndex = channelIndex;
+ return this;
+ }
+
+ /**
+ * Set the codec specific configuration for this Broadcast Channel.
+ *
+ * @param codecMetadata codec specific configuration for this Broadcast Channel
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setCodecMetadata(
+ @NonNull BluetoothLeAudioCodecConfigMetadata codecMetadata) {
+ mCodecMetadata = codecMetadata;
+ return this;
+ }
+
+ /**
+ * Build {@link BluetoothLeBroadcastChannel}.
+ *
+ * @return constructed {@link BluetoothLeBroadcastChannel}
+ * @throws IllegalArgumentException if the object cannot be built
+ * @hide
+ */
+ @SystemApi
+ public @NonNull BluetoothLeBroadcastChannel build() {
+ return new BluetoothLeBroadcastChannel(mIsSelected, mChannelIndex, mCodecMetadata);
+ }
+ }
+}
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java b/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java
new file mode 100644
index 0000000..1810818
--- /dev/null
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java
@@ -0,0 +1,486 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a Broadcast Source group and the associated information that is needed
+ * by Broadcast Audio Scan Service (BASS) to set up a Broadcast Sink.
+ *
+ * <p>For example, an LE Audio Broadcast Sink can use the information contained within an instance
+ * of this class to synchronize with an LE Audio Broadcast group in order to listen to audio from
+ * Broadcast subgroup using one or more Broadcast Channels.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BluetoothLeBroadcastMetadata implements Parcelable {
+ // Information needed for adding broadcast Source
+
+ // Optional: Identity address type
+ private final @BluetoothDevice.AddressType int mSourceAddressType;
+ // Optional: Must use identity address
+ private final BluetoothDevice mSourceDevice;
+ private final int mSourceAdvertisingSid;
+ private final int mBroadcastId;
+ private final int mPaSyncInterval;
+ private final boolean mIsEncrypted;
+ private final byte[] mBroadcastCode;
+
+ // BASE structure
+
+ // See Section 7 for description. Range: 0x000000 – 0xFFFFFF Units: μs
+ //All other values: RFU
+ private final int mPresentationDelayMicros;
+ // Number of subgroups used to group BISes present in the BIG
+ //Shall be at least 1, as defined by Rule 1
+ // Sub group info numSubGroup = mSubGroups.length
+ private final List<BluetoothLeBroadcastSubgroup> mSubgroups;
+
+ private BluetoothLeBroadcastMetadata(int sourceAddressType,
+ BluetoothDevice sourceDevice, int sourceAdvertisingSid, int broadcastId,
+ int paSyncInterval, boolean isEncrypted, byte[] broadcastCode, int presentationDelay,
+ List<BluetoothLeBroadcastSubgroup> subgroups) {
+ mSourceAddressType = sourceAddressType;
+ mSourceDevice = sourceDevice;
+ mSourceAdvertisingSid = sourceAdvertisingSid;
+ mBroadcastId = broadcastId;
+ mPaSyncInterval = paSyncInterval;
+ mIsEncrypted = isEncrypted;
+ mBroadcastCode = broadcastCode;
+ mPresentationDelayMicros = presentationDelay;
+ mSubgroups = subgroups;
+ }
+
+ /**
+ * Get the address type of the Broadcast Source.
+ *
+ * Can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC},
+ * {@link BluetoothDevice#ADDRESS_TYPE_RANDOM}
+ *
+ * @return address type of the Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ public @BluetoothDevice.AddressType int getSourceAddressType() {
+ return mSourceAddressType;
+ }
+
+ /**
+ * Get the MAC address of the Broadcast Source, which can be Public Device Address,
+ * Random Device Address, Public Identity Address or Random (static) Identity Address.
+ *
+ * @return MAC address of the Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ public @Nullable BluetoothDevice getSourceDevice() {
+ return mSourceDevice;
+ }
+
+ /**
+ * Get Advertising_SID subfield of the ADI field of the AUX_ADV_IND PDU or the
+ * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the
+ * Broadcast Source.
+ *
+ * @return 1-byte long Advertising_SID of the Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ public int getSourceAdvertisingSid() {
+ return mSourceAdvertisingSid;
+ }
+
+ /**
+ * Broadcast_ID of the Broadcast Source.
+ *
+ * @return 3-byte long Broadcast_ID of the Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ public int getBroadcastId() {
+ return mBroadcastId;
+ }
+
+ /**
+ * Indicated that Periodic Advertising Sync interval is unknown.
+ * @hide
+ */
+ @SystemApi
+ public static final int PA_SYNC_INTERVAL_UNKNOWN = 0xFFFF;
+
+ /**
+ * Get Periodic Advertising Sync interval of the broadcast Source.
+ *
+ * @return Periodic Advertising Sync interval of the broadcast Source,
+ * {@link #PA_SYNC_INTERVAL_UNKNOWN} if unknown
+ * @hide
+ */
+ @SystemApi
+ public int getPaSyncInterval() {
+ return mPaSyncInterval;
+ }
+
+ /**
+ * Return true if the Broadcast Source is encrypted.
+ *
+ * @return true if the Broadcast Source is encrypted
+ * @hide
+ */
+ @SystemApi
+ public boolean isEncrypted() {
+ return mIsEncrypted;
+ }
+
+ /**
+ * Get the Broadcast Code currently set for this Broadcast Source.
+ *
+ * Only needed when encryption is enabled
+ *
+ * <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.
+ *
+ * @return Broadcast Code currently set for this Broadcast Source, null if code is not required
+ * or code is currently unknown
+ * @hide
+ */
+ @SystemApi
+ public @Nullable byte[] getBroadcastCode() {
+ return mBroadcastCode;
+ }
+
+ /**
+ * Get the overall presentation delay in microseconds of this Broadcast Source.
+ *
+ * Presentation delay is defined in Section 7 of the Basic Audio Profile.
+ *
+ * @return presentation delay of this Broadcast Source in microseconds
+ * @hide
+ */
+ @SystemApi
+ public @IntRange(from = 0, to = 0xFFFFFF) int getPresentationDelayMicros() {
+ return mPresentationDelayMicros;
+ }
+
+ /**
+ * Get available subgroups in this broadcast source.
+ *
+ * @return list of subgroups in this broadcast source, which should contain at least one
+ * subgroup for each Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<BluetoothLeBroadcastSubgroup> getSubgroups() {
+ return mSubgroups;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mSourceAddressType);
+ if (mSourceDevice != null) {
+ out.writeInt(1);
+ out.writeTypedObject(mSourceDevice, 0);
+ } else {
+ // zero indicates missing mSourceDevice
+ out.writeInt(0);
+ }
+ out.writeInt(mSourceAdvertisingSid);
+ out.writeInt(mBroadcastId);
+ out.writeInt(mPaSyncInterval);
+ out.writeBoolean(mIsEncrypted);
+ if (mBroadcastCode != null) {
+ out.writeInt(mBroadcastCode.length);
+ out.writeByteArray(mBroadcastCode);
+ } else {
+ // -1 indicates missing broadcast code
+ out.writeInt(-1);
+ }
+ out.writeInt(mPresentationDelayMicros);
+ out.writeTypedList(mSubgroups);
+ }
+
+ /**
+ * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastMetadata} from parcel.
+ * @hide
+ */
+ @SystemApi
+ public static final @NonNull Parcelable.Creator<BluetoothLeBroadcastMetadata> CREATOR =
+ new Parcelable.Creator<BluetoothLeBroadcastMetadata>() {
+ public @NonNull BluetoothLeBroadcastMetadata createFromParcel(@NonNull Parcel in) {
+ Builder builder = new Builder();
+ final int sourceAddressType = in.readInt();
+ final int deviceExist = in.readInt();
+ BluetoothDevice sourceDevice = null;
+ if (deviceExist == 1) {
+ sourceDevice = in.readTypedObject(BluetoothDevice.CREATOR);
+ }
+ builder.setSourceDevice(sourceDevice, sourceAddressType);
+ builder.setSourceAdvertisingSid(in.readInt());
+ builder.setBroadcastId(in.readInt());
+ builder.setPaSyncInterval(in.readInt());
+ builder.setEncrypted(in.readBoolean());
+ final int codeLen = in.readInt();
+ byte[] broadcastCode = null;
+ if (codeLen != -1) {
+ broadcastCode = new byte[codeLen];
+ if (codeLen > 0) {
+ in.readByteArray(broadcastCode);
+ }
+ }
+ builder.setBroadcastCode(broadcastCode);
+ builder.setPresentationDelayMicros(in.readInt());
+ final List<BluetoothLeBroadcastSubgroup> subgroups = new ArrayList<>();
+ in.readTypedList(subgroups, BluetoothLeBroadcastSubgroup.CREATOR);
+ for (BluetoothLeBroadcastSubgroup subgroup : subgroups) {
+ builder.addSubgroup(subgroup);
+ }
+ return builder.build();
+ }
+
+ public @NonNull BluetoothLeBroadcastMetadata[] newArray(int size) {
+ return new BluetoothLeBroadcastMetadata[size];
+ }
+ };
+
+ private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
+
+ /**
+ * Builder for {@link BluetoothLeBroadcastMetadata}.
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private @BluetoothDevice.AddressType int mSourceAddressType =
+ BluetoothDevice.ADDRESS_TYPE_UNKNOWN;
+ private BluetoothDevice mSourceDevice = null;
+ private int mSourceAdvertisingSid = UNKNOWN_VALUE_PLACEHOLDER;
+ private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
+ private int mPaSyncInterval = UNKNOWN_VALUE_PLACEHOLDER;
+ private boolean mIsEncrypted = false;
+ private byte[] mBroadcastCode = null;
+ private int mPresentationDelayMicros = UNKNOWN_VALUE_PLACEHOLDER;
+ private List<BluetoothLeBroadcastSubgroup> mSubgroups = new ArrayList<>();
+
+ /**
+ * Create an empty builder.
+ * @hide
+ */
+ @SystemApi
+ public Builder() {}
+
+ /**
+ * Create a builder with copies of information from original object.
+ *
+ * @param original original object
+ * @hide
+ */
+ @SystemApi
+ public Builder(@NonNull BluetoothLeBroadcastMetadata original) {
+ mSourceAddressType = original.getSourceAddressType();
+ mSourceDevice = original.getSourceDevice();
+ mSourceAdvertisingSid = original.getSourceAdvertisingSid();
+ mBroadcastId = original.getBroadcastId();
+ mPaSyncInterval = original.getPaSyncInterval();
+ mIsEncrypted = original.isEncrypted();
+ mBroadcastCode = original.getBroadcastCode();
+ mPresentationDelayMicros = original.getPresentationDelayMicros();
+ mSubgroups = original.getSubgroups();
+ }
+
+
+ /**
+ * Set the address type and MAC address of the Broadcast Source.
+ *
+ * Address type can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC},
+ * {@link BluetoothDevice#ADDRESS_TYPE_RANDOM}
+ *
+ * MAC address can be Public Device Address, Random Device Address, Public Identity Address
+ * or Random (static) Identity Address
+ *
+ * @param sourceDevice source advertiser address
+ * @param sourceAddressType source advertiser address type
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setSourceDevice(@Nullable BluetoothDevice sourceDevice,
+ @BluetoothDevice.AddressType int sourceAddressType) {
+ mSourceDevice = sourceDevice;
+ mSourceAddressType = sourceAddressType;
+ return this;
+ }
+
+ /**
+ * Set Advertising_SID that is a subfield of the ADI field of the AUX_ADV_IND PDU or the
+ * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the
+ * Broadcast Source.
+ *
+ * @param sourceAdvertisingSid 1-byte long Advertising_SID of the Broadcast Source
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setSourceAdvertisingSid(int sourceAdvertisingSid) {
+ mSourceAdvertisingSid = sourceAdvertisingSid;
+ return this;
+ }
+
+ /**
+ * Set the Broadcast_ID of the Broadcast Source.
+ *
+ * @param broadcastId 3-byte long Broadcast_ID of the Broadcast Source
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setBroadcastId(int broadcastId) {
+ mBroadcastId = broadcastId;
+ return this;
+ }
+
+ /**
+ * Set Periodic Advertising Sync interval of the broadcast Source.
+ *
+ * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source,
+ * {@link #PA_SYNC_INTERVAL_UNKNOWN} if unknown
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setPaSyncInterval(int paSyncInterval) {
+ mPaSyncInterval = paSyncInterval;
+ return this;
+ }
+
+ /**
+ * Set whether the Broadcast Source should be encrypted.
+ *
+ * When setting up a Broadcast Source, if <var>isEncrypted</var> is true while
+ * <var>broadcastCode</var> is null, the implementation will automatically generate
+ * a Broadcast Code
+ *
+ * @param isEncrypted whether the Broadcast Source is encrypted
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setEncrypted(boolean isEncrypted) {
+ mIsEncrypted = isEncrypted;
+ return this;
+ }
+
+ /**
+ * Set the Broadcast Code currently set for this Broadcast Source.
+ *
+ * Only needed when encryption is enabled
+ *
+ * <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.
+ *
+ * @param broadcastCode Broadcast Code for this Broadcast Source, null if code is not
+ * required
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setBroadcastCode(@Nullable byte[] broadcastCode) {
+ mBroadcastCode = broadcastCode;
+ return this;
+ }
+
+ /**
+ * Set the overall presentation delay in microseconds of this Broadcast Source.
+ *
+ * Presentation delay is defined in Section 7 of the Basic Audio Profile.
+ *
+ * @param presentationDelayMicros presentation delay of this Broadcast Source in
+ * microseconds
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setPresentationDelayMicros(
+ @IntRange(from = 0, to = 0xFFFFFF) int presentationDelayMicros) {
+ mPresentationDelayMicros = presentationDelayMicros;
+ return this;
+ }
+
+ /**
+ * Add a subgroup to this broadcast source.
+ *
+ * @param subgroup {@link BluetoothLeBroadcastSubgroup} that contains a subgroup's metadata
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder addSubgroup(@NonNull BluetoothLeBroadcastSubgroup subgroup) {
+ mSubgroups.add(subgroup);
+ return this;
+ }
+
+ /**
+ * Clear subgroup list so that one can reset the builder after create it from an existing
+ * object.
+ *
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder clearSubgroup() {
+ mSubgroups.clear();
+ return this;
+ }
+
+ /**
+ * Build {@link BluetoothLeBroadcastMetadata}.
+ *
+ * @return {@link BluetoothLeBroadcastMetadata}
+ * @throws IllegalArgumentException if the object cannot be built
+ * @hide
+ */
+ @SystemApi
+ public @NonNull BluetoothLeBroadcastMetadata build() {
+ return new BluetoothLeBroadcastMetadata(mSourceAddressType, mSourceDevice,
+ mSourceAdvertisingSid, mBroadcastId, mPaSyncInterval, mIsEncrypted,
+ mBroadcastCode, mPresentationDelayMicros, mSubgroups);
+ }
+ }
+}
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastReceiveState.java b/framework/java/android/bluetooth/BluetoothLeBroadcastReceiveState.java
new file mode 100644
index 0000000..bab17ee
--- /dev/null
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastReceiveState.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The {@link BluetoothLeBroadcastReceiveState} is used by the BASS server to expose information
+ * about a Broadcast Source.
+ *
+ * It represents the current synchronization state of the server to
+ * a PA and/or a BIG containing one or more subgroups containing one or more BISes
+ * transmitted by that Broadcast Source. The Broadcast Receive State characteristic is also
+ * used to inform clients whether the server has detected that the BIS is encrypted, whether
+ * the server requires a Broadcast_Code, and whether the server is decrypting the BIS.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BluetoothLeBroadcastReceiveState implements Parcelable {
+ /**
+ * Periodic Advertising Synchronization state.
+ *
+ * <p>Periodic Advertising (PA) enables the LE Audio Broadcast Assistant to discover broadcast
+ * audio streams as well as the audio stream configuration on behalf of an LE Audio Broadcast
+ * Sink. This information can then be transferred to the LE Audio Broadcast Sink using the
+ * Periodic Advertising Synchronization Transfer (PAST) procedure.
+ *
+ * @hide
+ */
+ @IntDef(prefix = "PA_SYNC_STATE_",
+ value = {
+ PA_SYNC_STATE_IDLE,
+ PA_SYNC_STATE_SYNCINFO_REQUEST,
+ PA_SYNC_STATE_SYNCHRONIZED,
+ PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE,
+ PA_SYNC_STATE_NO_PAST
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PaSyncState {}
+
+ /**
+ * Indicates that the Broadcast Sink is not synchronized with the Periodic Advertisements (PA)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int PA_SYNC_STATE_IDLE = 0;
+
+ /**
+ * Indicates that the Broadcast Sink requested the Broadcast Assistant to synchronize with the
+ * Periodic Advertisements (PA).
+ *
+ * <p>This is also known as scan delegation or scan offloading.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int PA_SYNC_STATE_SYNCINFO_REQUEST = 1;
+
+ /**
+ * Indicates that the Broadcast Sink is synchronized with the Periodic Advertisements (PA).
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int PA_SYNC_STATE_SYNCHRONIZED = 2;
+
+ /**
+ * Indicates that the Broadcast Sink was unable to synchronize with the Periodic Advertisements
+ * (PA).
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE = 3;
+
+ /**
+ * Indicates that the Broadcast Sink should be synchronized with the Periodic Advertisements
+ * (PA) using the Periodic Advertisements Synchronization Transfer (PAST) procedure.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int PA_SYNC_STATE_NO_PAST = 4;
+
+ /**
+ * Indicates that the Broadcast Sink synchronization state is invalid.
+ *
+ * @hide
+ */
+ public static final int PA_SYNC_STATE_INVALID = 0xFFFF;
+
+ /** @hide */
+ @IntDef(
+ prefix = "BIG_ENCRYPTION_STATE_",
+ value = {
+ BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+ BIG_ENCRYPTION_STATE_CODE_REQUIRED,
+ BIG_ENCRYPTION_STATE_DECRYPTING,
+ BIG_ENCRYPTION_STATE_BAD_CODE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BigEncryptionState {}
+
+ /**
+ * Indicates that the Broadcast Sink is synchronized with an unencrypted audio stream from a
+ * Broadcast Source
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int BIG_ENCRYPTION_STATE_NOT_ENCRYPTED = 0;
+
+ /**
+ * Indicates that the Broadcast Sink needs a Broadcast Code to synchronize with an audio stream
+ * from a Broadcast Source, which was not provided when the audio stream from the Broadcast
+ * Source was added.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int BIG_ENCRYPTION_STATE_CODE_REQUIRED = 1;
+
+ /**
+ * Indicates that the Broadcast Sink is synchronized with an encrypted audio stream from a
+ * Broadcast Source.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int BIG_ENCRYPTION_STATE_DECRYPTING = 2;
+
+ /**
+ * Indicates that the Broadcast Sink is unable to decrypt an audio stream from a Broadcast
+ * Source due to an incorrect Broadcast Code.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int BIG_ENCRYPTION_STATE_BAD_CODE = 3;
+
+ /**
+ * Indicates that the Broadcast Sink encryption state is invalid.
+ *
+ * @hide
+ */
+ public static final int BIG_ENCRYPTION_STATE_INVALID = 0xFFFF;
+
+ private final int mSourceId;
+ private final @BluetoothDevice.AddressType int mSourceAddressType;
+ private final BluetoothDevice mSourceDevice;
+ private final int mSourceAdvertisingSid;
+ private final int mBroadcastId;
+ private final @PaSyncState int mPaSyncState;
+ private final @BigEncryptionState int mBigEncryptionState;
+ private final byte[] mBadCode;
+ private final int mNumSubgroups;
+ private final List<Long> mBisSyncState;
+ private final List<BluetoothLeAudioContentMetadata> mSubgroupMetadata;
+
+ /**
+ * Constructor to create a read-only {@link BluetoothLeBroadcastReceiveState} instance.
+ *
+ * @hide
+ */
+ public BluetoothLeBroadcastReceiveState(int sourceId, int sourceAddressType,
+ BluetoothDevice sourceDevice, int sourceAdvertisingSid, int broadcastId,
+ int paSyncState, int bigEncryptionState, byte[] badCode, int numSubgroups,
+ List<Long> bisSyncState,
+ List<BluetoothLeAudioContentMetadata> subgroupMetadata) {
+ mSourceId = sourceId;
+ mSourceAddressType = sourceAddressType;
+ mSourceDevice = sourceDevice;
+ mSourceAdvertisingSid = sourceAdvertisingSid;
+ mBroadcastId = broadcastId;
+ mPaSyncState = paSyncState;
+ mBigEncryptionState = bigEncryptionState;
+ mBadCode = badCode;
+ mNumSubgroups = numSubgroups;
+ mBisSyncState = bisSyncState;
+ mSubgroupMetadata = subgroupMetadata;
+ }
+
+ /**
+ * Get the source ID assigned by the BASS server
+ *
+ * Shall be unique for each instance of the Broadcast Receive State characteristic exposed by
+ * the server
+ *
+ * @return source ID assigned by the BASS server
+ * @hide
+ */
+ @SystemApi
+ public @IntRange(from = 0x00, to = 0xFF) int getSourceId() {
+ return mSourceId;
+ }
+
+ /**
+ * Get the address type of the Broadcast Source
+ *
+ * Can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} or
+ * {@link BluetoothDevice#ADDRESS_TYPE_RANDOM
+ *
+ * @return address type of the Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ public @BluetoothDevice.AddressType int getSourceAddressType() {
+ return mSourceAddressType;
+ }
+
+ /**
+ * Get the MAC address of the Broadcast Source, which can be Public Device Address,
+ * Random Device Address, Public Identity Address or Random (static) Identity Address
+ *
+ * @return MAC address of the Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ public @NonNull BluetoothDevice getSourceDevice() {
+ return mSourceDevice;
+ }
+
+ /**
+ * Get Advertising_SID subfield of the ADI field of the AUX_ADV_IND PDU or the
+ * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the
+ * Broadcast Source.
+ *
+ * @return 1-byte long Advertising_SID of the Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ public int getSourceAdvertisingSid() {
+ return mSourceAdvertisingSid;
+ }
+
+ /**
+ * Broadcast_ID of the Broadcast Source
+ *
+ * @return 3-byte long Broadcast_ID of the Broadcast Source
+ * @hide
+ */
+ @SystemApi
+ public int getBroadcastId() {
+ return mBroadcastId;
+ }
+
+ /**
+ * Get the Periodic Advertisement synchronization state between the Broadcast Sink and the
+ * Broadcast source
+ *
+ * Possible values are {@link #PA_SYNC_STATE_IDLE}, {@link #PA_SYNC_STATE_SYNCINFO_REQUEST},
+ * {@link #PA_SYNC_STATE_SYNCHRONIZED}, {@link #PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE},
+ * {@link #PA_SYNC_STATE_NO_PAST}
+ *
+ * @return Periodic Advertisement synchronization state
+ * @hide
+ */
+ @SystemApi
+ public @PaSyncState int getPaSyncState() {
+ return mPaSyncState;
+ }
+
+ /**
+ * Get the encryption state of a Broadcast Isochronous Group (BIG)
+ *
+ * Possible values are {@link #BIG_ENCRYPTION_STATE_NOT_ENCRYPTED},
+ * {@link #BIG_ENCRYPTION_STATE_CODE_REQUIRED}, {@link #BIG_ENCRYPTION_STATE_DECRYPTING},
+ * {@link #BIG_ENCRYPTION_STATE_DECRYPTING}, and {@link #BIG_ENCRYPTION_STATE_BAD_CODE}
+ *
+ * @return encryption state of a Broadcast Isochronous Group (BIG)
+ * @hide
+ */
+ @SystemApi
+ public @BigEncryptionState int getBigEncryptionState() {
+ return mBigEncryptionState;
+ }
+
+ /**
+ * If {@link #getBigEncryptionState()} returns {@link #BIG_ENCRYPTION_STATE_BAD_CODE}, this
+ * method returns the value of the incorrect 16-octet Broadcast Code that fails to decrypt
+ * an audio stream from a Broadcast Source.
+ *
+ * @return 16-octet Broadcast Code, or null if {@link #getBigEncryptionState()} does not return
+ * {@link #BIG_ENCRYPTION_STATE_BAD_CODE}
+ * @hide
+ */
+ @SystemApi
+ public @Nullable byte[] getBadCode() {
+ return mBadCode;
+ }
+
+ /**
+ * Get number of Broadcast subgroups being added to this sink
+ *
+ * @return number of Broadcast subgroups being added to this sink
+ */
+ public int getNumSubgroups() {
+ return mNumSubgroups;
+ }
+
+ /**
+ * Get a list of bitfield on whether a Broadcast Isochronous Stream (BIS) is synchronized
+ * between the sink and source
+ *
+ * The number of items in the returned list is the same as {@link #getNumSubgroups()}. For each
+ * subgroup, at most 31 BISes are available and their synchronization state is indicated by its
+ * bit value at the particular offset (i.e. Bit 0-30 = BIS_index[1-31])
+ *
+ * For example, if (BisSyncState & 0b1 << 5) != 0, BIS 5 is synchronized between source and sync
+ *
+ * There is a special case, 0xFFFFFFFF to indicate Broadcast Sink failed to synchronize to
+ * a particular subgroup
+ *
+ * @return a list of bitfield on whether a Broadcast Isochronous Stream (BIS) is synchronized
+ * between the sink and source
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<Long> getBisSyncState() {
+ return mBisSyncState;
+ }
+
+ /**
+ * Get metadata for every subgroup added to this Broadcast Sink
+ *
+ * The number of items in the returned list is the same as {@link #getNumSubgroups()}.
+ *
+ * @return metadata for every subgroup added to this Broadcast Sink
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<BluetoothLeAudioContentMetadata> getSubgroupMetadata() {
+ return mSubgroupMetadata;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mSourceId);
+ out.writeInt(mSourceAddressType);
+ out.writeTypedObject(mSourceDevice, 0);
+ out.writeInt(mSourceAdvertisingSid);
+ out.writeInt(mBroadcastId);
+ out.writeInt(mPaSyncState);
+ out.writeInt(mBigEncryptionState);
+
+ if (mBadCode != null) {
+ out.writeInt(mBadCode.length);
+ out.writeByteArray(mBadCode);
+ } else {
+ // -1 indicates that there is no "bad broadcast code"
+ out.writeInt(-1);
+ }
+ out.writeInt(mNumSubgroups);
+ out.writeList(mBisSyncState);
+ out.writeTypedList(mSubgroupMetadata);
+ }
+
+ /**
+ * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastReceiveState} from parcel.
+ * @hide
+ */
+ @SystemApi
+ public static final @NonNull Parcelable.Creator<BluetoothLeBroadcastReceiveState> CREATOR =
+ new Parcelable.Creator<BluetoothLeBroadcastReceiveState>() {
+ public @NonNull BluetoothLeBroadcastReceiveState createFromParcel(
+ @NonNull Parcel in) {
+ final int sourceId = in.readInt();
+ final int sourceAddressType = in.readInt();
+ final BluetoothDevice sourceDevice =
+ in.readTypedObject(BluetoothDevice.CREATOR);
+ final int sourceAdvertisingSid = in.readInt();
+ final int broadcastId = in.readInt();
+ final int paSyncState = in.readInt();
+ final int bigEncryptionState = in.readInt();
+ final int badCodeLen = in.readInt();
+ byte[] badCode = null;
+
+ if (badCodeLen != -1) {
+ badCode = new byte[badCodeLen];
+ if (badCodeLen > 0) {
+ in.readByteArray(badCode);
+ }
+ }
+ final byte numSubGroups = in.readByte();
+ final List<Long> bisSyncState =
+ in.readArrayList(Long.class.getClassLoader(), Long.class);
+ final List<BluetoothLeAudioContentMetadata> subgroupMetadata =
+ new ArrayList<>();
+ in.readTypedList(subgroupMetadata, BluetoothLeAudioContentMetadata.CREATOR);
+
+ return new BluetoothLeBroadcastReceiveState(
+ sourceId,
+ sourceAddressType,
+ sourceDevice,
+ sourceAdvertisingSid,
+ broadcastId,
+ paSyncState,
+ bigEncryptionState,
+ badCode,
+ numSubGroups,
+ bisSyncState,
+ subgroupMetadata);
+ }
+
+ public @NonNull BluetoothLeBroadcastReceiveState[] newArray(int size) {
+ return new BluetoothLeBroadcastReceiveState[size];
+ }
+ };
+}
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java b/framework/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java
deleted file mode 100644
index cb47280..0000000
--- a/framework/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java
+++ /dev/null
@@ -1,788 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * This class represents an LE Audio Broadcast Source and the associated information that is needed
- * by Broadcast Audio Scan Service (BASS) residing on a Scan Delegator.
- *
- * <p>For example, the Scan Delegator on an LE Audio Broadcast Sink can use the information
- * contained within an instance of this class to synchronize with an LE Audio Broadcast Source in
- * order to listen to a Broadcast Audio Stream.
- *
- * <p>BroadcastAssistant has a BASS client which facilitates scanning and discovery of Broadcast
- * Sources on behalf of say a Broadcast Sink. Upon successful discovery of one or more Broadcast
- * sources, this information needs to be communicated to the BASS Server residing within the Scan
- * Delegator on a Broadcast Sink. This is achieved using the Periodic Advertising Synchronization
- * Transfer (PAST) procedure. This procedure uses information contained within an instance of this
- * class.
- *
- * @hide
- */
-public final class BluetoothLeBroadcastSourceInfo implements Parcelable {
- private static final String TAG = "BluetoothLeBroadcastSourceInfo";
- private static final boolean DBG = true;
-
- /**
- * Constants representing Broadcast Source address types
- *
- * @hide
- */
- @IntDef(
- prefix = "LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_",
- value = {
- LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_PUBLIC,
- LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_RANDOM,
- LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface LeAudioBroadcastSourceAddressType {}
-
- /**
- * Represents a public address used by an LE Audio Broadcast Source
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_PUBLIC = 0;
-
- /**
- * Represents a random address used by an LE Audio Broadcast Source
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_RANDOM = 1;
-
- /**
- * Represents an invalid address used by an LE Audio Broadcast Seurce
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID = 0xFFFF;
-
- /**
- * Periodic Advertising Synchronization state
- *
- * <p>Periodic Advertising (PA) enables the LE Audio Broadcast Assistant to discover broadcast
- * audio streams as well as the audio stream configuration on behalf of an LE Audio Broadcast
- * Sink. This information can then be transferred to the LE Audio Broadcast Sink using the
- * Periodic Advertising Synchronizaton Transfer (PAST) procedure.
- *
- * @hide
- */
- @IntDef(
- prefix = "LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_",
- value = {
- LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IDLE,
- LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNCINFO_REQ,
- LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IN_SYNC,
- LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNC_FAIL,
- LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_NO_PAST
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface LeAudioBroadcastSinkPaSyncState {}
-
- /**
- * Indicates that the Broadcast Sink is not synchronized with the Periodic Advertisements (PA)
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IDLE = 0;
-
- /**
- * Indicates that the Broadcast Sink requested the Broadcast Assistant to synchronize with the
- * Periodic Advertisements (PA).
- *
- * <p>This is also known as scan delegation or scan offloading.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNCINFO_REQ = 1;
-
- /**
- * Indicates that the Broadcast Sink is synchronized with the Periodic Advertisements (PA).
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IN_SYNC = 2;
-
- /**
- * Indicates that the Broadcast Sink was unable to synchronize with the Periodic Advertisements
- * (PA).
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNC_FAIL = 3;
-
- /**
- * Indicates that the Broadcast Sink should be synchronized with the Periodic Advertisements
- * (PA) using the Periodic Advertisements Synchronization Transfert (PAST) procedure.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_NO_PAST = 4;
-
- /**
- * Indicates that the Broadcast Sink synchornization state is invalid.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID = 0xFFFF;
-
- /** @hide */
- @IntDef(
- prefix = "LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_",
- value = {
- LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED,
- LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_SYNCHRONIZED
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface LeAudioBroadcastSinkAudioSyncState {}
-
- /**
- * Indicates that the Broadcast Sink is not synchronized with a Broadcast Audio Stream.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED = 0;
-
- /**
- * Indicates that the Broadcast Sink is synchronized with a Broadcast Audio Stream.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_SYNCHRONIZED = 1;
-
- /**
- * Indicates that the Broadcast Sink audio synchronization state is invalid.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID = 0xFFFF;
-
- /** @hide */
- @IntDef(
- prefix = "LE_AUDIO_BROADCAST_SINK_ENC_STATE_",
- value = {
- LE_AUDIO_BROADCAST_SINK_ENC_STATE_NOT_ENCRYPTED,
- LE_AUDIO_BROADCAST_SINK_ENC_STATE_CODE_REQUIRED,
- LE_AUDIO_BROADCAST_SINK_ENC_STATE_DECRYPTING,
- LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface LeAudioBroadcastSinkEncryptionState {}
-
- /**
- * Indicates that the Broadcast Sink is synchronized with an unencrypted audio stream.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_NOT_ENCRYPTED = 0;
-
- /**
- * Indicates that the Broadcast Sink needs a Broadcast Code to synchronize with the audio
- * stream.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_CODE_REQUIRED = 1;
-
- /**
- * Indicates that the Broadcast Sink is synchronized with an encrypted audio stream.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_DECRYPTING = 2;
-
- /**
- * Indicates that the Broadcast Sink is unable to decrypt an audio stream due to an incorrect
- * Broadcast Code
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE = 3;
-
- /**
- * Indicates that the Broadcast Sink encryption state is invalid.
- *
- * @hide
- */
- public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID = 0xFF;
-
- /**
- * Represents an invalid LE Audio Broadcast Source ID
- *
- * @hide
- */
- public static final byte LE_AUDIO_BROADCAST_SINK_INVALID_SOURCE_ID = (byte) 0x00;
-
- /**
- * Represents an invalid Broadcast ID of a Broadcast Source
- *
- * @hide
- */
- public static final int INVALID_BROADCAST_ID = 0xFFFFFF;
-
- private byte mSourceId;
- private @LeAudioBroadcastSourceAddressType int mSourceAddressType;
- private BluetoothDevice mSourceDevice;
- private byte mSourceAdvSid;
- private int mBroadcastId;
- private @LeAudioBroadcastSinkPaSyncState int mPaSyncState;
- private @LeAudioBroadcastSinkEncryptionState int mEncryptionStatus;
- private @LeAudioBroadcastSinkAudioSyncState int mAudioSyncState;
- private byte[] mBadBroadcastCode;
- private byte mNumSubGroups;
- private Map<Integer, Integer> mSubgroupBisSyncState = new HashMap<Integer, Integer>();
- private Map<Integer, byte[]> mSubgroupMetadata = new HashMap<Integer, byte[]>();
-
- private String mBroadcastCode;
- private static final int BIS_NO_PREF = 0xFFFFFFFF;
- private static final int BROADCAST_CODE_SIZE = 16;
-
- /**
- * Constructor to create an Empty object of {@link BluetoothLeBroadcastSourceInfo } with the
- * given Source Id.
- *
- * <p>This is mainly used to represent the Empty Broadcast Source entries
- *
- * @param sourceId Source Id for this Broadcast Source info object
- * @hide
- */
- public BluetoothLeBroadcastSourceInfo(byte sourceId) {
- mSourceId = sourceId;
- mSourceAddressType = LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID;
- mSourceDevice = null;
- mSourceAdvSid = (byte) 0x00;
- mBroadcastId = INVALID_BROADCAST_ID;
- mPaSyncState = LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID;
- mAudioSyncState = LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID;
- mEncryptionStatus = LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID;
- mBadBroadcastCode = null;
- mNumSubGroups = 0;
- mBroadcastCode = null;
- }
-
- /*package*/ BluetoothLeBroadcastSourceInfo(
- byte sourceId,
- @LeAudioBroadcastSourceAddressType int addressType,
- @NonNull BluetoothDevice device,
- byte advSid,
- int broadcastId,
- @LeAudioBroadcastSinkPaSyncState int paSyncstate,
- @LeAudioBroadcastSinkEncryptionState int encryptionStatus,
- @LeAudioBroadcastSinkAudioSyncState int audioSyncstate,
- @Nullable byte[] badCode,
- byte numSubGroups,
- @NonNull Map<Integer, Integer> bisSyncState,
- @Nullable Map<Integer, byte[]> subgroupMetadata,
- @NonNull String broadcastCode) {
- mSourceId = sourceId;
- mSourceAddressType = addressType;
- mSourceDevice = device;
- mSourceAdvSid = advSid;
- mBroadcastId = broadcastId;
- mPaSyncState = paSyncstate;
- mEncryptionStatus = encryptionStatus;
- mAudioSyncState = audioSyncstate;
-
- if (badCode != null && badCode.length != 0) {
- mBadBroadcastCode = new byte[badCode.length];
- System.arraycopy(badCode, 0, mBadBroadcastCode, 0, badCode.length);
- }
- mNumSubGroups = numSubGroups;
- mSubgroupBisSyncState = new HashMap<Integer, Integer>(bisSyncState);
- mSubgroupMetadata = new HashMap<Integer, byte[]>(subgroupMetadata);
- mBroadcastCode = broadcastCode;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof BluetoothLeBroadcastSourceInfo) {
- BluetoothLeBroadcastSourceInfo other = (BluetoothLeBroadcastSourceInfo) o;
- return (other.mSourceId == mSourceId
- && other.mSourceAddressType == mSourceAddressType
- && other.mSourceDevice == mSourceDevice
- && other.mSourceAdvSid == mSourceAdvSid
- && other.mBroadcastId == mBroadcastId
- && other.mPaSyncState == mPaSyncState
- && other.mEncryptionStatus == mEncryptionStatus
- && other.mAudioSyncState == mAudioSyncState
- && Arrays.equals(other.mBadBroadcastCode, mBadBroadcastCode)
- && other.mNumSubGroups == mNumSubGroups
- && mSubgroupBisSyncState.equals(other.mSubgroupBisSyncState)
- && mSubgroupMetadata.equals(other.mSubgroupMetadata)
- && other.mBroadcastCode == mBroadcastCode);
- }
- return false;
- }
-
- /**
- * Checks if an instance of {@link BluetoothLeBroadcastSourceInfo} is empty.
- *
- * @hide
- */
- public boolean isEmpty() {
- boolean ret = false;
- if (mSourceAddressType == LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID
- && mSourceDevice == null
- && mSourceAdvSid == (byte) 0
- && mPaSyncState == LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID
- && mEncryptionStatus == LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID
- && mAudioSyncState == LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID
- && mBadBroadcastCode == null
- && mNumSubGroups == 0
- && mSubgroupBisSyncState.size() == 0
- && mSubgroupMetadata.size() == 0
- && mBroadcastCode == null) {
- ret = true;
- }
- return ret;
- }
-
- /**
- * Compares an instance of {@link BluetoothLeBroadcastSourceInfo} with the provided instance.
- *
- * @hide
- */
- public boolean matches(BluetoothLeBroadcastSourceInfo srcInfo) {
- boolean ret = false;
- if (srcInfo == null) {
- ret = false;
- } else {
- if (mSourceDevice == null) {
- if (mSourceAdvSid == srcInfo.getAdvertisingSid()
- && mSourceAddressType == srcInfo.getAdvAddressType()) {
- ret = true;
- }
- } else {
- if (mSourceDevice.equals(srcInfo.getSourceDevice())
- && mSourceAdvSid == srcInfo.getAdvertisingSid()
- && mSourceAddressType == srcInfo.getAdvAddressType()
- && mBroadcastId == srcInfo.getBroadcastId()) {
- ret = true;
- }
- }
- }
- return ret;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(
- mSourceId,
- mSourceAddressType,
- mSourceDevice,
- mSourceAdvSid,
- mBroadcastId,
- mPaSyncState,
- mEncryptionStatus,
- mAudioSyncState,
- mBadBroadcastCode,
- mNumSubGroups,
- mSubgroupBisSyncState,
- mSubgroupMetadata,
- mBroadcastCode);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public String toString() {
- return "{BluetoothLeBroadcastSourceInfo : mSourceId"
- + mSourceId
- + " addressType: "
- + mSourceAddressType
- + " sourceDevice: "
- + mSourceDevice
- + " mSourceAdvSid:"
- + mSourceAdvSid
- + " mBroadcastId:"
- + mBroadcastId
- + " mPaSyncState:"
- + mPaSyncState
- + " mEncryptionStatus:"
- + mEncryptionStatus
- + " mAudioSyncState:"
- + mAudioSyncState
- + " mBadBroadcastCode:"
- + mBadBroadcastCode
- + " mNumSubGroups:"
- + mNumSubGroups
- + " mSubgroupBisSyncState:"
- + mSubgroupBisSyncState
- + " mSubgroupMetadata:"
- + mSubgroupMetadata
- + " mBroadcastCode:"
- + mBroadcastCode
- + "}";
- }
-
- /**
- * Get the Source Id
- *
- * @return byte representing the Source Id, {@link
- * #LE_AUDIO_BROADCAST_ASSISTANT_INVALID_SOURCE_ID} if invalid
- * @hide
- */
- public byte getSourceId() {
- return mSourceId;
- }
-
- /**
- * Set the Source Id
- *
- * @param sourceId source Id
- * @hide
- */
- public void setSourceId(byte sourceId) {
- mSourceId = sourceId;
- }
-
- /**
- * Set the Broadcast Source device
- *
- * @param sourceDevice the Broadcast Source BluetoothDevice
- * @hide
- */
- public void setSourceDevice(@NonNull BluetoothDevice sourceDevice) {
- mSourceDevice = sourceDevice;
- }
-
- /**
- * Get the Broadcast Source BluetoothDevice
- *
- * @return Broadcast Source BluetoothDevice
- * @hide
- */
- public @NonNull BluetoothDevice getSourceDevice() {
- return mSourceDevice;
- }
-
- /**
- * Set the address type of the Broadcast Source advertisements
- *
- * @hide
- */
- public void setAdvAddressType(@LeAudioBroadcastSourceAddressType int addressType) {
- mSourceAddressType = addressType;
- }
-
- /**
- * Get the address type used by advertisements from the Broadcast Source.
- * BluetoothLeBroadcastSourceInfo Object
- *
- * @hide
- */
- @LeAudioBroadcastSourceAddressType
- public int getAdvAddressType() {
- return mSourceAddressType;
- }
-
- /**
- * Set the advertising SID of the Broadcast Source advertisement.
- *
- * @param advSid advertising SID of the Broadcast Source
- * @hide
- */
- public void setAdvertisingSid(byte advSid) {
- mSourceAdvSid = advSid;
- }
-
- /**
- * Get the advertising SID of the Broadcast Source advertisement.
- *
- * @return advertising SID of the Broadcast Source
- * @hide
- */
- public byte getAdvertisingSid() {
- return mSourceAdvSid;
- }
-
- /**
- * Get the Broadcast ID of the Broadcast Source.
- *
- * @return broadcast ID
- * @hide
- */
- public int getBroadcastId() {
- return mBroadcastId;
- }
-
- /**
- * Set the Periodic Advertising (PA) Sync State.
- *
- * @hide
- */
- /*package*/ void setPaSyncState(@LeAudioBroadcastSinkPaSyncState int paSyncState) {
- mPaSyncState = paSyncState;
- }
-
- /**
- * Get the Periodic Advertising (PA) Sync State
- *
- * @hide
- */
- public @LeAudioBroadcastSinkPaSyncState int getMetadataSyncState() {
- return mPaSyncState;
- }
-
- /**
- * Set the audio sync state
- *
- * @hide
- */
- /*package*/ void setAudioSyncState(@LeAudioBroadcastSinkAudioSyncState int audioSyncState) {
- mAudioSyncState = audioSyncState;
- }
-
- /**
- * Get the audio sync state
- *
- * @hide
- */
- public @LeAudioBroadcastSinkAudioSyncState int getAudioSyncState() {
- return mAudioSyncState;
- }
-
- /**
- * Set the encryption status
- *
- * @hide
- */
- /*package*/ void setEncryptionStatus(
- @LeAudioBroadcastSinkEncryptionState int encryptionStatus) {
- mEncryptionStatus = encryptionStatus;
- }
-
- /**
- * Get the encryption status
- *
- * @hide
- */
- public @LeAudioBroadcastSinkEncryptionState int getEncryptionStatus() {
- return mEncryptionStatus;
- }
-
- /**
- * Get the incorrect broadcast code that the Scan delegator used to decrypt the Broadcast Audio
- * Stream and failed.
- *
- * <p>This code is valid only if {@link #getEncryptionStatus} returns {@link
- * #LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE}
- *
- * @return byte array containing bad broadcast value, null if the current encryption status is
- * not {@link #LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE}
- * @hide
- */
- public @Nullable byte[] getBadBroadcastCode() {
- return mBadBroadcastCode;
- }
-
- /**
- * Get the number of subgroups.
- *
- * @return number of subgroups
- * @hide
- */
- public byte getNumberOfSubGroups() {
- return mNumSubGroups;
- }
-
- public @NonNull Map<Integer, Integer> getSubgroupBisSyncState() {
- return mSubgroupBisSyncState;
- }
-
- public void setSubgroupBisSyncState(@NonNull Map<Integer, Integer> bisSyncState) {
- mSubgroupBisSyncState = new HashMap<Integer, Integer>(bisSyncState);
- }
-
- /*package*/ void setBroadcastCode(@NonNull String broadcastCode) {
- mBroadcastCode = broadcastCode;
- }
-
- /**
- * Get the broadcast code
- *
- * @return
- * @hide
- */
- public @NonNull String getBroadcastCode() {
- return mBroadcastCode;
- }
-
- /**
- * Set the broadcast ID
- *
- * @param broadcastId broadcast ID of the Broadcast Source
- * @hide
- */
- public void setBroadcastId(int broadcastId) {
- mBroadcastId = broadcastId;
- }
-
- private void writeSubgroupBisSyncStateToParcel(
- @NonNull Parcel dest, @NonNull Map<Integer, Integer> subgroupBisSyncState) {
- dest.writeInt(subgroupBisSyncState.size());
- for (Map.Entry<Integer, Integer> entry : subgroupBisSyncState.entrySet()) {
- dest.writeInt(entry.getKey());
- dest.writeInt(entry.getValue());
- }
- }
-
- private static void readSubgroupBisSyncStateFromParcel(
- @NonNull Parcel in, @NonNull Map<Integer, Integer> subgroupBisSyncState) {
- int size = in.readInt();
-
- for (int i = 0; i < size; i++) {
- Integer key = in.readInt();
- Integer value = in.readInt();
- subgroupBisSyncState.put(key, value);
- }
- }
-
- private void writeSubgroupMetadataToParcel(
- @NonNull Parcel dest, @Nullable Map<Integer, byte[]> subgroupMetadata) {
- if (subgroupMetadata == null) {
- dest.writeInt(0);
- return;
- }
-
- dest.writeInt(subgroupMetadata.size());
- for (Map.Entry<Integer, byte[]> entry : subgroupMetadata.entrySet()) {
- dest.writeInt(entry.getKey());
- byte[] metadata = entry.getValue();
- if (metadata != null) {
- dest.writeInt(metadata.length);
- dest.writeByteArray(metadata);
- }
- }
- }
-
- private static void readSubgroupMetadataFromParcel(
- @NonNull Parcel in, @NonNull Map<Integer, byte[]> subgroupMetadata) {
- int size = in.readInt();
-
- for (int i = 0; i < size; i++) {
- Integer key = in.readInt();
- Integer metaDataLen = in.readInt();
- byte[] metadata = null;
- if (metaDataLen != 0) {
- metadata = new byte[metaDataLen];
- in.readByteArray(metadata);
- }
- subgroupMetadata.put(key, metadata);
- }
- }
-
- public static final @NonNull Parcelable.Creator<BluetoothLeBroadcastSourceInfo> CREATOR =
- new Parcelable.Creator<BluetoothLeBroadcastSourceInfo>() {
- public @NonNull BluetoothLeBroadcastSourceInfo createFromParcel(
- @NonNull Parcel in) {
- final byte sourceId = in.readByte();
- final int sourceAddressType = in.readInt();
- final BluetoothDevice sourceDevice =
- in.readTypedObject(BluetoothDevice.CREATOR);
- final byte sourceAdvSid = in.readByte();
- final int broadcastId = in.readInt();
- final int paSyncState = in.readInt();
- final int audioSyncState = in.readInt();
- final int encryptionStatus = in.readInt();
- final int badBroadcastLen = in.readInt();
- byte[] badBroadcastCode = null;
-
- if (badBroadcastLen > 0) {
- badBroadcastCode = new byte[badBroadcastLen];
- in.readByteArray(badBroadcastCode);
- }
- final byte numSubGroups = in.readByte();
- final String broadcastCode = in.readString();
- Map<Integer, Integer> subgroupBisSyncState = new HashMap<Integer, Integer>();
- readSubgroupBisSyncStateFromParcel(in, subgroupBisSyncState);
- Map<Integer, byte[]> subgroupMetadata = new HashMap<Integer, byte[]>();
- readSubgroupMetadataFromParcel(in, subgroupMetadata);
-
- BluetoothLeBroadcastSourceInfo srcInfo =
- new BluetoothLeBroadcastSourceInfo(
- sourceId,
- sourceAddressType,
- sourceDevice,
- sourceAdvSid,
- broadcastId,
- paSyncState,
- encryptionStatus,
- audioSyncState,
- badBroadcastCode,
- numSubGroups,
- subgroupBisSyncState,
- subgroupMetadata,
- broadcastCode);
- return srcInfo;
- }
-
- public @NonNull BluetoothLeBroadcastSourceInfo[] newArray(int size) {
- return new BluetoothLeBroadcastSourceInfo[size];
- }
- };
-
- @Override
- public void writeToParcel(@NonNull Parcel out, int flags) {
- out.writeByte(mSourceId);
- out.writeInt(mSourceAddressType);
- out.writeTypedObject(mSourceDevice, 0);
- out.writeByte(mSourceAdvSid);
- out.writeInt(mBroadcastId);
- out.writeInt(mPaSyncState);
- out.writeInt(mAudioSyncState);
- out.writeInt(mEncryptionStatus);
-
- if (mBadBroadcastCode != null) {
- out.writeInt(mBadBroadcastCode.length);
- out.writeByteArray(mBadBroadcastCode);
- } else {
- // zero indicates that there is no "bad broadcast code"
- out.writeInt(0);
- }
- out.writeByte(mNumSubGroups);
- out.writeString(mBroadcastCode);
- writeSubgroupBisSyncStateToParcel(out, mSubgroupBisSyncState);
- writeSubgroupMetadataToParcel(out, mSubgroupMetadata);
- }
-
- private static void log(@NonNull String msg) {
- if (DBG) {
- Log.d(TAG, msg);
- }
- }
-}
-;
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java b/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java
new file mode 100644
index 0000000..273ac43
--- /dev/null
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class contains the subgroup level information as defined in the BASE structure of Basic
+ * Audio profile.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BluetoothLeBroadcastSubgroup implements Parcelable {
+ private final long mCodecId;
+ private final BluetoothLeAudioCodecConfigMetadata mCodecSpecificConfig;
+ private final BluetoothLeAudioContentMetadata mContentMetadata;
+ private final boolean mNoChannelPreference;
+ private final List<BluetoothLeBroadcastChannel> mChannels;
+
+ private BluetoothLeBroadcastSubgroup(long codecId,
+ BluetoothLeAudioCodecConfigMetadata codecSpecificConfig,
+ BluetoothLeAudioContentMetadata contentMetadata, boolean noChannelPreference,
+ List<BluetoothLeBroadcastChannel> channels) {
+ mCodecId = codecId;
+ mCodecSpecificConfig = codecSpecificConfig;
+ mContentMetadata = contentMetadata;
+ mNoChannelPreference = noChannelPreference;
+ mChannels = channels;
+ }
+
+ /**
+ * Get the codec ID field as defined by the Basic Audio Profile.
+ *
+ * The codec ID field has 5 octets, with
+ * - Octet 0: Coding_Format as defined in Bluetooth Assigned Numbers
+ * - Octet 1-2: Company ID as defined in Bluetooth Assigned Numbers
+ * Shall be 0x0000 if octet 0 != 0xFF
+ * - Octet 3-4: Vendor-specific codec ID
+ * Shall be 0x0000 if octet 0 != 0xFF
+ *
+ * @return 5-byte codec ID field in Java long format
+ * @hide
+ */
+ @SystemApi
+ public long getCodecId() {
+ return mCodecId;
+ }
+
+ /**
+ * Get codec specific config metadata for this subgroup.
+ *
+ * @return codec specific config metadata for this subgroup
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public BluetoothLeAudioCodecConfigMetadata getCodecSpecificConfig() {
+ return mCodecSpecificConfig;
+ }
+
+ /**
+ * Get content metadata for this Broadcast Source subgroup.
+ *
+ * @return content metadata for this Broadcast Source subgroup
+ * @hide
+ */
+ @SystemApi
+ public @NonNull BluetoothLeAudioContentMetadata getContentMetadata() {
+ return mContentMetadata;
+ }
+
+ /**
+ * Indicate if Broadcast Sink should have no Broadcast Channel (BIS) preference.
+ *
+ * Only used by Broadcast Assistant and Sink. Ignored by Broadcast Source
+ *
+ * @return true if Broadcast Sink should have no Broadcast Channel (BIS) preference
+ * @hide
+ */
+ @SystemApi
+ public boolean isNoChannelPreference() {
+ return mNoChannelPreference;
+ }
+
+ /**
+ * Get list of Broadcast Channels included in this Broadcast subgroup.
+ *
+ * Each Broadcast Channel represents a Broadcast Isochronous Stream (BIS)
+ *
+ * A Broadcast subgroup should contain at least 1 Broadcast Channel
+ *
+ * @return list of Broadcast Channels included in this Broadcast subgroup
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<BluetoothLeBroadcastChannel> getChannels() {
+ return mChannels;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mCodecId);
+ out.writeTypedObject(mCodecSpecificConfig, 0);
+ out.writeTypedObject(mContentMetadata, 0);
+ out.writeBoolean(mNoChannelPreference);
+ out.writeTypedList(mChannels);
+ }
+
+ /**
+ * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastSubgroup} from parcel.
+ * @hide
+ */
+ @SystemApi
+ public static final @NonNull Parcelable.Creator<BluetoothLeBroadcastSubgroup> CREATOR =
+ new Parcelable.Creator<BluetoothLeBroadcastSubgroup>() {
+ public @NonNull BluetoothLeBroadcastSubgroup createFromParcel(@NonNull Parcel in) {
+ Builder builder = new Builder();
+ builder.setCodecId(in.readLong());
+ builder.setCodecSpecificConfig(in.readTypedObject(
+ BluetoothLeAudioCodecConfigMetadata.CREATOR));
+ builder.setNoChannelPreference(in.readBoolean());
+ List<BluetoothLeBroadcastChannel> channels = new ArrayList<>();
+ in.readTypedList(channels, BluetoothLeBroadcastChannel.CREATOR);
+ for (BluetoothLeBroadcastChannel channel : channels) {
+ builder.addChannel(channel);
+ }
+ return builder.build();
+ }
+
+ public @NonNull BluetoothLeBroadcastSubgroup[] newArray(int size) {
+ return new BluetoothLeBroadcastSubgroup[size];
+ }
+ };
+
+ private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
+
+ /**
+ * Builder for {@link BluetoothLeBroadcastSubgroup}.
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private long mCodecId = UNKNOWN_VALUE_PLACEHOLDER;
+ private BluetoothLeAudioCodecConfigMetadata mCodecSpecificConfig = null;
+ private BluetoothLeAudioContentMetadata mContentMetadata = null;
+ private boolean mNoChannelPreference = false;
+ private List<BluetoothLeBroadcastChannel> mChannels = new ArrayList<>();
+
+ /**
+ * Create an empty constructor.
+ * @hide
+ */
+ @SystemApi
+ public Builder() {}
+
+ /**
+ * Create a builder with copies of information from original object.
+ *
+ * @param original original object
+ * @hide
+ */
+ @SystemApi
+ public Builder(@NonNull BluetoothLeBroadcastSubgroup original) {
+ mCodecId = original.getCodecId();
+ mCodecSpecificConfig = original.getCodecSpecificConfig();
+ mContentMetadata = original.getContentMetadata();
+ mNoChannelPreference = original.isNoChannelPreference();
+ mChannels = original.getChannels();
+ }
+
+
+ /**
+ * Set the codec ID field as defined by the Basic Audio Profile.
+ *
+ * The codec ID field has 5 octets, with
+ * - Octet 0: Coding_Format as defined in Bluetooth Assigned Numbers
+ * - Octet 1-2: Company ID as defined in Bluetooth Assigned Numbers
+ * Shall be 0x0000 if octet 0 != 0xFF
+ * - Octet 3-4: Vendor-specific codec ID
+ * Shall be 0x0000 if octet 0 != 0xFF
+ *
+ * @param codecId 5-byte codec ID field in Java long format
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setCodecId(long codecId) {
+ mCodecId = codecId;
+ return this;
+ }
+
+ /**
+ * Set codec specific config metadata for this subgroup.
+ *
+ * @param codecSpecificConfig codec specific config metadata for this subgroup
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setCodecSpecificConfig(
+ @NonNull BluetoothLeAudioCodecConfigMetadata codecSpecificConfig) {
+ mCodecSpecificConfig = codecSpecificConfig;
+ return this;
+ }
+
+ /**
+ * Set content metadata for this Broadcast Source subgroup.
+ *
+ * @param contentMetadata content metadata for this Broadcast Source subgroup
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setContentMetadata(
+ @NonNull BluetoothLeAudioContentMetadata contentMetadata) {
+ mContentMetadata = contentMetadata;
+ return this;
+ }
+
+ /**
+ * Set if Broadcast Sink should have no Broadcast Channel (BIS) preference.
+ *
+ * Only used by Broadcast Assistant and Sink. Ignored by Broadcast Source
+ *
+ * @param isNoChannelPreference true if Broadcast Sink should have no Broadcast Channel
+ * (BIS) preference
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setNoChannelPreference(boolean isNoChannelPreference) {
+ mNoChannelPreference = isNoChannelPreference;
+ return this;
+ }
+
+ /**
+ * Add a Broadcast Channel to this Broadcast subgroup.
+ *
+ * Each Broadcast Channel represents a Broadcast Isochronous Stream (BIS)
+ *
+ * A Broadcast subgroup should contain at least 1 Broadcast Channel
+ *
+ * @param channel a Broadcast Channel to be added to this Broadcast subgroup
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder addChannel(@NonNull BluetoothLeBroadcastChannel channel) {
+ mChannels.add(channel);
+ return this;
+ }
+
+ /**
+ * Clear channel list so that one can reset the builder after create it from an existing
+ * object.
+ *
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder clearChannel() {
+ mChannels.clear();
+ return this;
+ }
+
+ /**
+ * Build {@link BluetoothLeBroadcastSubgroup}.
+ *
+ * @return constructed {@link BluetoothLeBroadcastSubgroup}
+ * @throws IllegalArgumentException if the object cannot be built
+ * @hide
+ */
+ @SystemApi
+ public @NonNull BluetoothLeBroadcastSubgroup build() {
+ return new BluetoothLeBroadcastSubgroup(mCodecId, mCodecSpecificConfig,
+ mContentMetadata, mNoChannelPreference, mChannels);
+ }
+ }
+}
diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java
index 1fa2bcf..c29a671 100644
--- a/framework/java/android/bluetooth/BluetoothProfile.java
+++ b/framework/java/android/bluetooth/BluetoothProfile.java
@@ -237,6 +237,7 @@
*
* @hide
*/
+ @SystemApi
int LE_AUDIO_BROADCAST = 26;
/**
@@ -253,12 +254,20 @@
int HAP_CLIENT = 28;
/**
+ * LE Audio Broadcast Assistant
+ *
+ * @hide
+ */
+ @SystemApi
+ int LE_AUDIO_BROADCAST_ASSISTANT = 29;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 28;
+ int MAX_PROFILE_ID = 29;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java
index 5017198..01e30db 100644
--- a/framework/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java
@@ -133,6 +133,94 @@
public static final int ERROR_TIMEOUT = 15;
/**
+ * Indicates that some local application caused the event.
+ * @hide
+ */
+ @SystemApi
+ public static final int REASON_LOCAL_APP_REQUEST = 16;
+
+ /**
+ * Indicate that this change was initiated by the Bluetooth implementation on this device
+ * @hide
+ */
+ @SystemApi
+ public static final int REASON_LOCAL_STACK_REQUEST = 17;
+
+ /**
+ * Indicate that this change was initiated by the remote device.
+ * @hide
+ */
+ @SystemApi
+ public static final int REASON_REMOTE_REQUEST = 18;
+
+ /**
+ * Indicates that the local system policy caused the change, such as privacy policy, power
+ * management policy, permission changes, and more.
+ * @hide
+ */
+ @SystemApi
+ public static final int REASON_SYSTEM_POLICY = 19;
+
+ /**
+ * Indicates that an underlying hardware incurred some error maybe try again later or toggle
+ * the hardware state.
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_HARDWARE_GENERIC = 20;
+
+ /**
+ * Indicates that the operation failed due to bad API input parameter that is not covered
+ * by other more detailed error code
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_BAD_PARAMETERS = 21;
+
+ /**
+ * Indicate that there is not enough local resource to perform the requested operation
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_LOCAL_NOT_ENOUGH_RESOURCES = 22;
+
+ /**
+ * Indicate that a remote device does not have enough resource to perform the requested
+ * operation
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_REMOTE_NOT_ENOUGH_RESOURCES = 23;
+
+ /**
+ * Indicates that the remote rejected this operation for reasons not covered above
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_REMOTE_OPERATION_REJECTED = 24;
+
+ /**
+ * Indicates that there is an underlying link error between the local and remote devices.
+ *
+ * Maybe try again later or disconnect and retry.
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_REMOTE_LINK_ERROR = 25;
+
+ /**
+ * A generic error code to indicate that the system is already in a target state that an API
+ * tries to request.
+ *
+ * For example, this error code will be delivered if someone tries to stop scanning when
+ * scan has already stopped, or start scanning when scan has already started.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_ALREADY_IN_TARGET_STATE = 26;
+
+ /**
* A GATT writeCharacteristic request is not permitted on the remote device.
*/
public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 200;
@@ -279,66 +367,6 @@
public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109;
/**
- * Indicates that setting the LE Audio Broadcast mode failed.
- * <p>
- * Example solution: Change parameters and try again. If error persists, the app can report
- * telemetry and/or log the error in a bugreport.
- *
- * @hide
- */
- public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED = 1110;
-
- /**
- * Indicates that setting a new encryption key for Bluetooth LE Audio Broadcast Source failed.
- * <p>
- * Example solution: Change parameters and try again. If error persists, the app can report
- * telemetry and/or log the error in a bugreport.
- *
- * @hide
- */
- public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED = 1111;
-
- /**
- * Indicates that connecting to a remote Broadcast Audio Scan Service failed.
- * <p>
- * Example solution: Change parameters and try again. If error persists, the app can report
- * telemetry and/or log the error in a bugreport.
- *
- * @hide
- */
- public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_CONNECT_FAILED = 1112;
-
- /**
- * Indicates that disconnecting from a remote Broadcast Audio Scan Service failed.
- * <p>
- * Example solution: Change parameters and try again. If error persists, the app can report
- * telemetry and/or log the error in a bugreport.
- *
- * @hide
- */
- public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_DISCONNECT_FAILED = 1113;
-
- /**
- * Indicates that enabling LE Audio Broadcast encryption failed
- * <p>
- * Example solution: Change parameters and try again. If error persists, the app can report
- * telemetry and/or log the error in a bugreport.
- *
- * @hide
- */
- public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED = 1114;
-
- /**
- * Indicates that disabling LE Audio Broadcast encryption failed
- * <p>
- * Example solution: Change parameters and try again. If error persists, the app can report
- * telemetry and/or log the error in a bugreport.
- *
- * @hide
- */
- public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED = 1115;
-
- /**
* Indicates that there is already one device for which SCO audio is connected or connecting.
*
* @hide
@@ -370,6 +398,61 @@
@SystemApi
public static final int ERROR_CALL_ACTIVE = 1119;
+ // LE audio related return codes reserved from 1200 to 1300
+
+ /**
+ * Indicates that the broadcast ID cannot be found among existing Broadcast Sources.
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_LE_BROADCAST_INVALID_BROADCAST_ID = 1200;
+
+ /**
+ * Indicates that encryption code entered does not meet the specification requirement
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_LE_BROADCAST_INVALID_CODE = 1201;
+
+ /**
+ * Indicates that the source ID cannot be found in the given Broadcast sink device
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID = 1202;
+
+ /**
+ * Indicates that the same Broadcast Source is already added to the Broadcast Sink
+ *
+ * Broadcast Source is identified by their advertising SID and broadcast ID
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_LE_BROADCAST_ASSISTANT_DUPLICATE_ADDITION = 1203;
+
+
+ /**
+ * Indicates that the program info in a {@link BluetoothLeAudioContentMetadata} is not valid
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_LE_CONTENT_METADATA_INVALID_PROGRAM_INFO = 1204;
+
+ /**
+ * Indicates that the language code in a {@link BluetoothLeAudioContentMetadata} is not valid
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_LE_CONTENT_METADATA_INVALID_LANGUAGE = 1205;
+
+ /**
+ * Indicates that operation failed due to other {@link BluetoothLeAudioContentMetadata} related
+ * issues not covered by other reason codes.
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_LE_CONTENT_METADATA_INVALID_OTHER = 1206;
+
/**
* Indicates that the RFCOMM listener could not be started due to the requested UUID already
* being in use.
@@ -422,7 +505,7 @@
public static final int RFCOMM_LISTENER_NO_SOCKET_AVAILABLE = 2005;
/**
- * Indicates that an unknown error has occurred has occurred.
+ * Indicates that an unknown error has occurred.
*/
public static final int ERROR_UNKNOWN = Integer.MAX_VALUE;
}