diff options
3 files changed, 217 insertions, 0 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java index 08f7806207db..f22bdaf55ce4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -89,11 +89,14 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { "com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_STATE_CHANGE"; public static final String ACTION_LE_AUDIO_SHARING_DEVICE_CONNECTED = "com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_DEVICE_CONNECTED"; + public static final String ACTION_LE_AUDIO_PRIVATE_BROADCAST_RECEIVED = + "com.android.settings.action.BLUETOOTH_LE_AUDIO_PRIVATE_BROADCAST_RECEIVED"; public static final String EXTRA_LE_AUDIO_SHARING_STATE = "BLUETOOTH_LE_AUDIO_SHARING_STATE"; public static final String EXTRA_BLUETOOTH_DEVICE = "BLUETOOTH_DEVICE"; public static final String EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE = "BT_DEVICE_TO_AUTO_ADD_SOURCE"; public static final String EXTRA_START_LE_AUDIO_SHARING = "START_LE_AUDIO_SHARING"; public static final String EXTRA_PAIR_AND_JOIN_SHARING = "PAIR_AND_JOIN_SHARING"; + public static final String EXTRA_PRIVATE_BROADCAST_RECEIVE_DATA = "RECEIVE_DATA"; public static final String BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID = "bluetooth_le_broadcast_primary_device_group_id"; public static final int BROADCAST_STATE_UNKNOWN = 0; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt new file mode 100644 index 000000000000..a284d2010195 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2025 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 com.android.settingslib.bluetooth + +import android.bluetooth.BluetoothDevice +import android.os.Parcel +import android.os.Parcelable +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.STREAMING +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED + +/** + * Data class representing information received in a private broadcast. + * This class encapsulates details about the sink device, source ID, broadcast ID, and the + * broadcast source state. + * + * @param sink The [BluetoothDevice] acting as the sink. + * @param sourceId The ID of the audio source. + * @param broadcastId The ID of the broadcast source. + * @param programInfo The program info string of the broadcast source. + * @param state The current state of the broadcast source. + */ +data class PrivateBroadcastReceiveData( + val sink: BluetoothDevice?, + val sourceId: Int = -1, + val broadcastId: Int = -1, + val programInfo: String = "", + val state: LocalBluetoothLeBroadcastSourceState?, +) : Parcelable { + + override fun describeContents(): Int = 0 + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeParcelable(sink, flags) + parcel.writeInt(sourceId) + parcel.writeInt(broadcastId) + parcel.writeString(programInfo) + parcel.writeSerializable(state) + } + + companion object { + @JvmField + val CREATOR: Parcelable.Creator<PrivateBroadcastReceiveData> = + object : Parcelable.Creator<PrivateBroadcastReceiveData> { + override fun createFromParcel(parcel: Parcel) = + parcel.run { + PrivateBroadcastReceiveData( + sink = readParcelable( + BluetoothDevice::class.java.classLoader, + BluetoothDevice::class.java + ), + sourceId = readInt(), + broadcastId = readInt(), + programInfo = readString() ?: "", + state = readSerializable( + LocalBluetoothLeBroadcastSourceState::class.java.classLoader, + LocalBluetoothLeBroadcastSourceState::class.java + ) + ) + } + override fun newArray(size: Int): Array<PrivateBroadcastReceiveData?> { + return arrayOfNulls(size) + } + } + + fun PrivateBroadcastReceiveData.isValid(): Boolean { + return sink != null + && sourceId != -1 + && broadcastId != -1 + && (state == STREAMING + || state == PAUSED + || state == DECRYPTION_FAILED) + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt new file mode 100644 index 000000000000..5fd67a16a305 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2025 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 com.android.settingslib.bluetooth + +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.os.Parcel +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState +import com.android.settingslib.bluetooth.PrivateBroadcastReceiveData.Companion.isValid +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class PrivateBroadcastReceiveDataTest { + + @Test + fun parcelable() { + val original = PrivateBroadcastReceiveData( + sink = sink, + sourceId = 1, + broadcastId = 2, + programInfo = "Test Program", + state = LocalBluetoothLeBroadcastSourceState.STREAMING + ) + + val parcel = Parcel.obtain() + original.writeToParcel(parcel, 0) + parcel.setDataPosition(0) + + val recreated = PrivateBroadcastReceiveData.CREATOR.createFromParcel(parcel) + + assertEquals(original, recreated) + } + + @Test + fun isValid_validData() { + val data = PrivateBroadcastReceiveData( + sink = sink, + sourceId = 1, + broadcastId = 2, + state = LocalBluetoothLeBroadcastSourceState.STREAMING + ) + assertTrue(data.isValid()) + } + + @Test + fun isValid_nullSink() { + val data = PrivateBroadcastReceiveData( + sink = null, + sourceId = 1, + broadcastId = 2, + state = LocalBluetoothLeBroadcastSourceState.STREAMING + ) + assertFalse(data.isValid()) + } + + @Test + fun isValid_invalidSourceId() { + val data = PrivateBroadcastReceiveData( + sink = sink, + sourceId = -1, + broadcastId = 2, + state = LocalBluetoothLeBroadcastSourceState.STREAMING + ) + assertFalse(data.isValid()) + } + + @Test + fun isValid_invalidBroadcastId() { + val data = PrivateBroadcastReceiveData( + sink = sink, + sourceId = 1, + broadcastId = -1, + state = LocalBluetoothLeBroadcastSourceState.STREAMING + ) + assertFalse(data.isValid()) + } + + @Test + fun isValid_nullState() { + val data = PrivateBroadcastReceiveData( + sink = sink, + sourceId = 1, + broadcastId = 2, + state = null + ) + assertFalse(data.isValid()) + } + + @Test + fun isValid_correctStates() { + assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.STREAMING).isValid()) + assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.PAUSED).isValid()) + assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED).isValid()) + } + + private companion object { + const val TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1" + + val sink: BluetoothDevice = + BluetoothAdapter.getDefaultAdapter().getRemoteLeDevice( + TEST_DEVICE_ADDRESS, + BluetoothDevice.ADDRESS_TYPE_RANDOM + ) + } +} |