diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpConfiguration.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpConfiguration.aidl new file mode 100644 index 0000000000..9e67b1520d --- /dev/null +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpConfiguration.aidl @@ -0,0 +1,41 @@ +/* + * Copyright 2023 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.bluetooth.audio; +@VintfStability +parcelable A2dpConfiguration { + int remoteSeid; + android.hardware.bluetooth.audio.CodecId id; + android.hardware.bluetooth.audio.CodecParameters parameters; + byte[] configuration; +} diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpConfigurationHint.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpConfigurationHint.aidl new file mode 100644 index 0000000000..0a5b489321 --- /dev/null +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpConfigurationHint.aidl @@ -0,0 +1,41 @@ +/* + * Copyright 2023 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.bluetooth.audio; +@VintfStability +parcelable A2dpConfigurationHint { + byte[6] bdAddr; + android.hardware.bluetooth.audio.AudioContext audioContext; + @nullable android.hardware.bluetooth.audio.CodecId codecId; + @nullable android.hardware.bluetooth.audio.CodecParameters codecParameters; +} diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpRemoteCapabilities.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpRemoteCapabilities.aidl new file mode 100644 index 0000000000..9c1e97173a --- /dev/null +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpRemoteCapabilities.aidl @@ -0,0 +1,40 @@ +/* + * Copyright 2023 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.bluetooth.audio; +@VintfStability +parcelable A2dpRemoteCapabilities { + int seid; + android.hardware.bluetooth.audio.CodecId id; + byte[] capabilities; +} diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpStatus.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpStatus.aidl new file mode 100644 index 0000000000..ac22e2519f --- /dev/null +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpStatus.aidl @@ -0,0 +1,65 @@ +/* + * Copyright 2023 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.bluetooth.audio; +@Backing(type="byte") @VintfStability +enum A2dpStatus { + OK = 0, + BAD_LENGTH = 0x11u8, + BAD_PAYLOAD_FORMAT = 0x18u8, + INVALID_CODEC_TYPE = 0xC1u8, + NOT_SUPPORTED_CODEC_TYPE = 0xC2u8, + INVALID_SAMPLING_FREQUENCY = 0xC3u8, + NOT_SUPPORTED_SAMPLING_FREQUENCY = 0xC4u8, + INVALID_CHANNEL_MODE = 0xC5u8, + NOT_SUPPORTED_CHANNEL_MODE = 0xC6u8, + INVALID_SUBBANDS = 0xC7u8, + NOT_SUPPORTED_SUBBANDS = 0xC8u8, + INVALID_ALLOCATION_METHOD = 0xC9u8, + NOT_SUPPORTED_ALLOCATION_METHOD = 0xCAu8, + INVALID_MINIMUM_BITPOOL_VALUE = 0xCBu8, + NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE = 0xCCu8, + INVALID_MAXIMUM_BITPOOL_VALUE = 0xCDu8, + NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE = 0xCEu8, + NOT_SUPPORTED_VBR = 0xD3u8, + NOT_SUPPORTED_BIT_RATE = 0xD5u8, + INVALID_OBJECT_TYPE = 0xD6u8, + NOT_SUPPORTED_OBJECT_TYPE = 0xD7u8, + INVALID_CHANNELS = 0xD8u8, + NOT_SUPPORTED_CHANNELS = 0xD9u8, + INVALID_BLOCK_LENGTH = 0xDDu8, + INVALID_CODEC_PARAMETER = 0xE2u8, + NOT_SUPPORTED_CODEC_PARAMETER = 0xE3u8, + INVALID_DRC = 0xE4u8, + NOT_SUPPORTED_DRC = 0xE5u8, +} diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpStreamConfiguration.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpStreamConfiguration.aidl new file mode 100644 index 0000000000..ff5a1bc298 --- /dev/null +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/A2dpStreamConfiguration.aidl @@ -0,0 +1,41 @@ +/* + * Copyright 2023 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.bluetooth.audio; +@VintfStability +parcelable A2dpStreamConfiguration { + int peerMtu; + @nullable byte[1] cpHeaderScmst; + android.hardware.bluetooth.audio.CodecId codecId; + byte[] configuration; +} diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AudioConfiguration.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AudioConfiguration.aidl index edb9ee27f5..2c402678b1 100644 --- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AudioConfiguration.aidl +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/AudioConfiguration.aidl @@ -39,4 +39,5 @@ union AudioConfiguration { android.hardware.bluetooth.audio.LeAudioConfiguration leAudioConfig; android.hardware.bluetooth.audio.LeAudioBroadcastConfiguration leAudioBroadcastConfig; android.hardware.bluetooth.audio.HfpConfiguration hfpConfig; + android.hardware.bluetooth.audio.A2dpStreamConfiguration a2dp; } diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecParameters.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecParameters.aidl new file mode 100644 index 0000000000..60cf82a9e3 --- /dev/null +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecParameters.aidl @@ -0,0 +1,45 @@ +/* + * Copyright 2023 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.bluetooth.audio; +@VintfStability +parcelable CodecParameters { + android.hardware.bluetooth.audio.ChannelMode channelMode; + int samplingFrequencyHz; + int bitdepth; + int minBitrate; + int maxBitrate; + boolean lowLatency; + boolean lossless; + byte[] vendorSpecificParameters; +} diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/IBluetoothAudioProvider.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/IBluetoothAudioProvider.aidl index 267af0f4b4..ccf5524bf1 100644 --- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/IBluetoothAudioProvider.aidl +++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/IBluetoothAudioProvider.aidl @@ -40,4 +40,6 @@ interface IBluetoothAudioProvider { void streamSuspended(in android.hardware.bluetooth.audio.BluetoothAudioStatus status); void updateAudioConfiguration(in android.hardware.bluetooth.audio.AudioConfiguration audioConfig); void setLowLatencyModeAllowed(in boolean allowed); + android.hardware.bluetooth.audio.A2dpStatus parseA2dpConfiguration(in android.hardware.bluetooth.audio.CodecId codecId, in byte[] configuration, out android.hardware.bluetooth.audio.CodecParameters codecParameters); + @nullable android.hardware.bluetooth.audio.A2dpConfiguration getA2dpConfiguration(in List remoteA2dpCapabilities, in android.hardware.bluetooth.audio.A2dpConfigurationHint hint); } diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpConfiguration.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpConfiguration.aidl new file mode 100644 index 0000000000..a7fd9ff014 --- /dev/null +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpConfiguration.aidl @@ -0,0 +1,43 @@ +/* + * Copyright 2023 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.hardware.bluetooth.audio; + +import android.hardware.bluetooth.audio.CodecId; +import android.hardware.bluetooth.audio.CodecParameters; + +/** + * A2DP Service Configuration + */ +@VintfStability +parcelable A2dpConfiguration { + /** + * Remote Stream Endpoint Identifier + */ + int remoteSeid; + + /** + * Codec Selection and configuration, in a generic way with `parameters` + * and as defined by A2DP for codec interoperability requirements, with + * `configuration`. Using `id.a2dp`, the format is given by the `Codec + * Specific Information Elements` [A2DP - 4.3-6.2], and using `id.vendor`, + * by `Vendor Specific Value` [A2DP - 4.7.2]. + * In any case, this byte array is limited by the framework to 128 Bytes. + */ + CodecId id; + CodecParameters parameters; + byte[] configuration; +} diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpConfigurationHint.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpConfigurationHint.aidl new file mode 100644 index 0000000000..f707a8ac90 --- /dev/null +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpConfigurationHint.aidl @@ -0,0 +1,42 @@ +/* + * Copyright 2023 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.hardware.bluetooth.audio; + +import android.hardware.bluetooth.audio.AudioContext; +import android.hardware.bluetooth.audio.CodecId; +import android.hardware.bluetooth.audio.CodecParameters; + +/** + * A2DP Configuration Hints + */ +@VintfStability +parcelable A2dpConfigurationHint { + /** + * Bluetooth Device Address, intended to be used for interoperabilities. + */ + byte[6] bdAddr; + + /** + * Audio configuration hints: + * - The starting audio context of the session + * - An optional preference of codec and / or parameters + */ + + AudioContext audioContext; + @nullable CodecId codecId; + @nullable CodecParameters codecParameters; +} diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpRemoteCapabilities.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpRemoteCapabilities.aidl new file mode 100644 index 0000000000..87277f150d --- /dev/null +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpRemoteCapabilities.aidl @@ -0,0 +1,39 @@ +/* + * Copyright 2023 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.hardware.bluetooth.audio; + +import android.hardware.bluetooth.audio.CodecId; + +/** + * A2DP Remote Capabilites + */ +@VintfStability +parcelable A2dpRemoteCapabilities { + /** + * Remote Stream Endpoint identifier + */ + int seid; + + /** + * Codec Identifier and `capabilities` as defined by A2DP for codec + * interoperability requirements. Using `id.a2dp`, the format is given + * by the `Codec Specific Information Elements` [A2DP - 4.3-6.2], and + * using `id.vendor`, by `Vendor Specific Value` [A2DP - 4.7.2]. + */ + CodecId id; + byte[] capabilities; +} diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpStatus.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpStatus.aidl new file mode 100644 index 0000000000..8eba3c9571 --- /dev/null +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpStatus.aidl @@ -0,0 +1,61 @@ +/* + * Copyright 2023 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.hardware.bluetooth.audio; + +@VintfStability +@Backing(type="byte") +enum A2dpStatus { + + OK = 0, + + /** + * Error codes defined by AVDTP [AVDTP - 8.20.6.2] + */ + + BAD_LENGTH = 0x11u8, + BAD_PAYLOAD_FORMAT = 0x18u8, + + /** + * Error codecs defined by A2DP for AVDTP Interoperability [A2DP - 5.1.3] + */ + + INVALID_CODEC_TYPE = 0xC1u8, + NOT_SUPPORTED_CODEC_TYPE = 0xC2u8, + INVALID_SAMPLING_FREQUENCY = 0xC3u8, + NOT_SUPPORTED_SAMPLING_FREQUENCY = 0xC4u8, + INVALID_CHANNEL_MODE = 0xC5u8, + NOT_SUPPORTED_CHANNEL_MODE = 0xC6u8, + INVALID_SUBBANDS = 0xC7u8, + NOT_SUPPORTED_SUBBANDS = 0xC8u8, + INVALID_ALLOCATION_METHOD = 0xC9u8, + NOT_SUPPORTED_ALLOCATION_METHOD = 0xCAu8, + INVALID_MINIMUM_BITPOOL_VALUE = 0xCBu8, + NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE = 0xCCu8, + INVALID_MAXIMUM_BITPOOL_VALUE = 0xCDu8, + NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE = 0xCEu8, + NOT_SUPPORTED_VBR = 0xD3u8, + NOT_SUPPORTED_BIT_RATE = 0xD5u8, + INVALID_OBJECT_TYPE = 0xD6u8, + NOT_SUPPORTED_OBJECT_TYPE = 0xD7u8, + INVALID_CHANNELS = 0xD8u8, + NOT_SUPPORTED_CHANNELS = 0xD9u8, + INVALID_BLOCK_LENGTH = 0xDDu8, + INVALID_CODEC_PARAMETER = 0xE2u8, + NOT_SUPPORTED_CODEC_PARAMETER = 0xE3u8, + INVALID_DRC = 0xE4u8, + NOT_SUPPORTED_DRC = 0xE5u8, +} diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpStreamConfiguration.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpStreamConfiguration.aidl new file mode 100644 index 0000000000..2a0c4d84ab --- /dev/null +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/A2dpStreamConfiguration.aidl @@ -0,0 +1,43 @@ +/* + * Copyright 2023 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.hardware.bluetooth.audio; + +import android.hardware.bluetooth.audio.CodecId; + +@VintfStability +parcelable A2dpStreamConfiguration { + /** + * Peer MTU (16 bits) + */ + int peerMtu; + + /** + * Optional SCMS-T Content Protection header + * that precedes audio content when enabled [A2DP - 3.2.1-2]. + * The content protection byte is defined by [Assigned Number - 6.3.2]. + */ + @nullable byte[1] cpHeaderScmst; + + /** + * Codec Identifier and `configuration` as defined by A2DP for codec + * interoperability requirements. Using `codecId.a2dp`, the format is given + * by the `Codec Specific Information Elements` [A2DP - 4.3-6.2], and + * using `codecId.vendor`, by `Vendor Specific Value` [A2DP - 4.7.2]. + */ + CodecId codecId; + byte[] configuration; +} diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/AudioConfiguration.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/AudioConfiguration.aidl index b7f7f292ea..5317dfbadb 100644 --- a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/AudioConfiguration.aidl +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/AudioConfiguration.aidl @@ -16,6 +16,7 @@ package android.hardware.bluetooth.audio; +import android.hardware.bluetooth.audio.A2dpStreamConfiguration; import android.hardware.bluetooth.audio.CodecConfiguration; import android.hardware.bluetooth.audio.HfpConfiguration; import android.hardware.bluetooth.audio.LeAudioBroadcastConfiguration; @@ -32,4 +33,5 @@ union AudioConfiguration { LeAudioConfiguration leAudioConfig; LeAudioBroadcastConfiguration leAudioBroadcastConfig; HfpConfiguration hfpConfig; + A2dpStreamConfiguration a2dp; } diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecParameters.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecParameters.aidl new file mode 100644 index 0000000000..b6f8a94b4a --- /dev/null +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecParameters.aidl @@ -0,0 +1,65 @@ +/* + * Copyright 2023 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.hardware.bluetooth.audio; + +import android.hardware.bluetooth.audio.ChannelMode; + +/** + * Used to exchange generic codec parameters between the stack and the provider. + */ +@VintfStability +parcelable CodecParameters { + /** + * PCM related parameters: + * - Mono, Dual-Mono or Stereo + * - Sampling frequencies, in Hz. + * - Fixed point resolution, basically 16, 24 or 32 bits by samples. + * The value 32 should be used for floating point representation.. + */ + ChannelMode channelMode; + int samplingFrequencyHz; + int bitdepth; + + /** + * Encoding parameters: + * + * - Bitrate limits on a frame basis, defined in bits per second. + * The encoder bitrate mode can be encoded following this rule: + * . minBitrate equals to maxBitrate for constant bitrate + * . minBitrate set to 0, for VBR with peak bitrate at maxBitratre value. + * . minBitrate greater than 0, for ABR, the bitrate of the stream varies + * between minBitrate to maxBitrate according to link quality. + * The 0 value for both means "undefined" or "don't care". + * + * - Low-latency configuration privileged + * - Lossless effort indication. The 'False' value can be used as "don't care" + */ + int minBitrate; + int maxBitrate; + + boolean lowLatency; + boolean lossless; + + /** + * Vendor specific parameters, inserted in the Vendor Specific HCI Command + * `Start A2DP Offload` as it is. The stack operates as a pass-through; + * the data SHALL NOT be inspected nor written by the client. + * The size is limited to 128 bytes by the client; a larger size is + * interpreted as a zero-sized buffer. + */ + byte[] vendorSpecificParameters; +} diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/IBluetoothAudioProvider.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/IBluetoothAudioProvider.aidl index 558173ea35..b5c8a31ce5 100644 --- a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/IBluetoothAudioProvider.aidl +++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/IBluetoothAudioProvider.aidl @@ -16,8 +16,14 @@ package android.hardware.bluetooth.audio; +import android.hardware.bluetooth.audio.A2dpConfiguration; +import android.hardware.bluetooth.audio.A2dpConfigurationHint; +import android.hardware.bluetooth.audio.A2dpRemoteCapabilities; +import android.hardware.bluetooth.audio.A2dpStatus; import android.hardware.bluetooth.audio.AudioConfiguration; import android.hardware.bluetooth.audio.BluetoothAudioStatus; +import android.hardware.bluetooth.audio.CodecId; +import android.hardware.bluetooth.audio.CodecParameters; import android.hardware.bluetooth.audio.IBluetoothAudioPort; import android.hardware.bluetooth.audio.LatencyMode; import android.hardware.common.fmq.MQDescriptor; @@ -92,4 +98,30 @@ interface IBluetoothAudioProvider { * mode, the API will be called with supported is false. */ void setLowLatencyModeAllowed(in boolean allowed); + + /** + * Validate and parse an A2DP Configuration, + * shall be used with A2DP session types + * + * @param codecId Identify the codec + * @param The configuration as defined by the A2DP's `Codec Specific + * Information Elements`, or `Vendor Specific Value` when CodecId + * format is set to `VENDOR`. + * @param codecParameters result of parsing, when the validation succeeded. + * @return A2DP Status of the parsing + */ + A2dpStatus parseA2dpConfiguration( + in CodecId codecId, in byte[] configuration, out CodecParameters codecParameters); + + /** + * Return a configuration, from a list of remote Capabilites, + * shall be used with A2DP session types + * + * @param remoteCapabilities The capabilities of the remote device + * @param hint Hint on selection (audio context and/or codec) + * @return The requested configuration. A null value value is returned + * when no suitable configuration has been found. + */ + @nullable A2dpConfiguration getA2dpConfiguration( + in List remoteA2dpCapabilities, in A2dpConfigurationHint hint); } diff --git a/bluetooth/audio/aidl/default/A2dpBits.h b/bluetooth/audio/aidl/default/A2dpBits.h new file mode 100644 index 0000000000..f467c9588f --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpBits.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 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. + */ + +#pragma once + +namespace aidl::android::hardware::bluetooth::audio { + +class A2dpBits { + const uint8_t* cdata_; + uint8_t* data_; + + public: + A2dpBits(const std::vector& vector) : cdata_(vector.data()) {} + + A2dpBits(std::vector& vector) + : cdata_(vector.data()), data_(vector.data()) {} + + struct Range { + const int first, len; + constexpr Range(int first, int last) + : first(first), len(last - first + 1) {} + constexpr Range(int index) : first(index), len(1) {} + }; + + constexpr bool get(int bit) const { + return (cdata_[bit >> 3] >> (7 - (bit & 7))) & 1; + } + + constexpr unsigned get(const Range& range) const { + unsigned v(0); + for (int i = 0; i < range.len; i++) + v |= get(range.first + i) << ((range.len - 1) - i); + return v; + } + + constexpr void set(int bit, int value = 1) { + uint8_t m = 1 << (7 - (bit & 7)); + if (value) + data_[bit >> 3] |= m; + else + data_[bit >> 3] &= ~m; + } + + constexpr void set(const Range& range, int value) { + for (int i = 0; i < range.len; i++) + set(range.first + i, (value >> ((range.len - 1) - i)) & 1); + } + + constexpr int find_active_bit(const Range& range) const { + unsigned v = get(range); + int i = 0; + for (; i < range.len && ((v >> i) & 1) == 0; i++) + ; + return i < range.len && (v ^ (1 << i)) == 0 + ? range.first + (range.len - 1) - i + : -1; + } +}; + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp index 2d0d8c9e96..ba7a89d8be 100644 --- a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp @@ -22,6 +22,10 @@ #include #include +#include "A2dpOffloadCodecAac.h" +#include "A2dpOffloadCodecFactory.h" +#include "A2dpOffloadCodecSbc.h" + namespace aidl { namespace android { namespace hardware { @@ -48,19 +52,44 @@ ndk::ScopedAStatus A2dpOffloadAudioProvider::startSession( const std::shared_ptr& host_if, const AudioConfiguration& audio_config, const std::vector& latency_modes, DataMQDesc* _aidl_return) { - if (audio_config.getTag() != AudioConfiguration::a2dpConfig) { - LOG(WARNING) << __func__ << " - Invalid Audio Configuration=" - << audio_config.toString(); - *_aidl_return = DataMQDesc(); - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } - if (!BluetoothAudioCodecs::IsOffloadCodecConfigurationValid( - session_type_, audio_config.get())) { + if (audio_config.getTag() == AudioConfiguration::Tag::a2dp) { + auto a2dp_config = audio_config.get(); + A2dpStatus a2dp_status = A2dpStatus::NOT_SUPPORTED_CODEC_TYPE; + + if (a2dp_config.codecId == + A2dpOffloadCodecSbc::GetInstance()->GetCodecId()) { + SbcParameters sbc_parameters; + a2dp_status = A2dpOffloadCodecSbc::GetInstance()->ParseConfiguration( + a2dp_config.configuration, &sbc_parameters); + + } else if (a2dp_config.codecId == + A2dpOffloadCodecAac::GetInstance()->GetCodecId()) { + AacParameters aac_parameters; + a2dp_status = A2dpOffloadCodecAac::GetInstance()->ParseConfiguration( + a2dp_config.configuration, &aac_parameters); + } + if (a2dp_status != A2dpStatus::OK) { + LOG(WARNING) << __func__ << " - Invalid Audio Configuration=" + << audio_config.toString(); + *_aidl_return = DataMQDesc(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + } else if (audio_config.getTag() == AudioConfiguration::Tag::a2dpConfig) { + if (!BluetoothAudioCodecs::IsOffloadCodecConfigurationValid( + session_type_, + audio_config.get())) { + LOG(WARNING) << __func__ << " - Invalid Audio Configuration=" + << audio_config.toString(); + *_aidl_return = DataMQDesc(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + } else { LOG(WARNING) << __func__ << " - Invalid Audio Configuration=" << audio_config.toString(); *_aidl_return = DataMQDesc(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + return BluetoothAudioProvider::startSession( host_if, audio_config, latency_modes, _aidl_return); } @@ -73,6 +102,36 @@ ndk::ScopedAStatus A2dpOffloadAudioProvider::onSessionReady( return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus A2dpOffloadAudioProvider::parseA2dpConfiguration( + const CodecId& codec_id, const std::vector& configuration, + CodecParameters* codec_parameters, A2dpStatus* _aidl_return) { + auto codec = A2dpOffloadCodecFactory::GetInstance()->GetCodec(codec_id); + if (!codec) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << " - CodecId=" << codec_id.toString() << " is not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + *_aidl_return = codec->ParseConfiguration(configuration, codec_parameters); + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus A2dpOffloadAudioProvider::getA2dpConfiguration( + const std::vector& remote_a2dp_capabilities, + const A2dpConfigurationHint& hint, + std::optional* _aidl_return) { + *_aidl_return = std::nullopt; + A2dpConfiguration avdtp_configuration; + + if (A2dpOffloadCodecFactory::GetInstance()->GetConfiguration( + remote_a2dp_capabilities, hint, &avdtp_configuration)) + *_aidl_return = + std::make_optional(std::move(avdtp_configuration)); + + return ndk::ScopedAStatus::ok(); +} + } // namespace audio } // namespace bluetooth } // namespace hardware diff --git a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.h b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.h index e6f188bda6..7cc6deeada 100644 --- a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.h +++ b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.h @@ -34,7 +34,16 @@ class A2dpOffloadAudioProvider : public BluetoothAudioProvider { const std::shared_ptr& host_if, const AudioConfiguration& audio_config, const std::vector& latency_modes, - DataMQDesc* _aidl_return); + DataMQDesc* _aidl_return) override; + + ndk::ScopedAStatus parseA2dpConfiguration( + const CodecId& codec_id, const std::vector& configuration, + CodecParameters* codec_parameters, A2dpStatus* _aidl_return) override; + + ndk::ScopedAStatus getA2dpConfiguration( + const std::vector& remote_a2dp_capabilities, + const A2dpConfigurationHint& hint, + std::optional* _aidl_return) override; private: ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override; diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodec.h b/bluetooth/audio/aidl/default/A2dpOffloadCodec.h new file mode 100644 index 0000000000..7ed5872346 --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodec.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 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. + */ + +#pragma once + +#include +#include +#include + +#include "BluetoothAudioProviderFactory.h" + +namespace aidl::android::hardware::bluetooth::audio { + +class A2dpOffloadCodec { + protected: + A2dpOffloadCodec(const CodecInfo& info) : info(info) {} + virtual ~A2dpOffloadCodec() {} + + public: + const CodecInfo& info; + + const CodecId& GetCodecId() const { return info.id; } + + virtual A2dpStatus ParseConfiguration( + const std::vector& configuration, + CodecParameters* codec_parameters) const = 0; + + virtual bool BuildConfiguration( + const std::vector& remote_capabilities, + const std::optional& hint, + std::vector* configuration) const = 0; +}; + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.cpp b/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.cpp new file mode 100644 index 0000000000..0f5533a4d9 --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.cpp @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2023 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. + */ + +#include "A2dpOffloadCodecAac.h" + +#include "A2dpBits.h" + +namespace aidl::android::hardware::bluetooth::audio { + +/** + * AAC Local Capabilities + */ + +enum : bool { + kEnableObjectTypeMpeg2AacLc = true, + kEnableObjectTypeMpeg4AacLc = true, +}; + +enum : bool { + kEnableSamplingFrequency44100 = true, + kEnableSamplingFrequency48000 = true, + kEnableSamplingFrequency88200 = false, + kEnableSamplingFrequency96000 = false, +}; + +enum : bool { + kEnableChannels1 = true, + kEnableChannels2 = true, +}; + +enum : bool { + kEnableVbrSupported = true, +}; + +enum : int { + kBitdepth = 24, +}; + +/** + * AAC Signaling format [A2DP - 4.5] + */ + +// clang-format off + +constexpr A2dpBits::Range kObjectType ( 0, 6 ); +constexpr A2dpBits::Range kDrcEnable ( 7 ); +constexpr A2dpBits::Range kSamplingFrequency ( 8, 19 ); +constexpr A2dpBits::Range kChannels ( 20, 23 ); +constexpr A2dpBits::Range kVbrSupported ( 24 ); +constexpr A2dpBits::Range kBitrate ( 25, 47 ); +constexpr size_t kCapabilitiesSize = 48/8; + +// clang-format on + +enum { + kObjectTypeMpeg2AacLc = kObjectType.first, + kObjectTypeMpeg4AacLc, + kObjectTypeMpeg4AacLtp, + kObjectTypeMpeg4AacScalable, + kObjectTypeMpeg4AacHeV1, + kObjectTypeMpeg4AacHeV2, + kObjectTypeMpeg4AacEldV2 +}; + +enum { + kSamplingFrequency8000 = kSamplingFrequency.first, + kSamplingFrequency11025, + kSamplingFrequency12000, + kSamplingFrequency16000, + kSamplingFrequency22050, + kSamplingFrequency24000, + kSamplingFrequency32000, + kSamplingFrequency44100, + kSamplingFrequency48000, + kSamplingFrequency64000, + kSamplingFrequency88200, + kSamplingFrequency96000 +}; + +enum { kChannels1 = kChannels.first, kChannels2, kChannels51, kChannels71 }; + +/** + * AAC Conversion functions + */ + +static AacParameters::ObjectType GetObjectTypeEnum(int object_type) { + switch (object_type) { + case kObjectTypeMpeg2AacLc: + return AacParameters::ObjectType::MPEG2_AAC_LC; + case kObjectTypeMpeg4AacLc: + default: + return AacParameters::ObjectType::MPEG4_AAC_LC; + } +} + +static int GetSamplingFrequencyBit(int32_t sampling_frequency) { + switch (sampling_frequency) { + case 8000: + return kSamplingFrequency8000; + case 11025: + return kSamplingFrequency11025; + case 12000: + return kSamplingFrequency12000; + case 16000: + return kSamplingFrequency16000; + case 22050: + return kSamplingFrequency22050; + case 24000: + return kSamplingFrequency24000; + case 32000: + return kSamplingFrequency32000; + case 44100: + return kSamplingFrequency44100; + case 48000: + return kSamplingFrequency48000; + case 64000: + return kSamplingFrequency64000; + case 88200: + return kSamplingFrequency88200; + case 96000: + return kSamplingFrequency96000; + default: + return -1; + } +} + +static int32_t GetSamplingFrequencyValue(int sampling_frequency) { + switch (sampling_frequency) { + case kSamplingFrequency8000: + return 8000; + case kSamplingFrequency11025: + return 11025; + case kSamplingFrequency12000: + return 12000; + case kSamplingFrequency16000: + return 16000; + case kSamplingFrequency22050: + return 22050; + case kSamplingFrequency24000: + return 24000; + case kSamplingFrequency32000: + return 32000; + case kSamplingFrequency44100: + return 44100; + case kSamplingFrequency48000: + return 48000; + case kSamplingFrequency64000: + return 64000; + case kSamplingFrequency88200: + return 88200; + case kSamplingFrequency96000: + return 96000; + default: + return 0; + } +} + +static int GetChannelsBit(ChannelMode channel_mode) { + switch (channel_mode) { + case ChannelMode::MONO: + return kChannels1; + case ChannelMode::STEREO: + return kChannels2; + default: + return -1; + } +} + +static ChannelMode GetChannelModeEnum(int channel_mode) { + switch (channel_mode) { + case kChannels1: + return ChannelMode::MONO; + case kChannels2: + return ChannelMode::STEREO; + default: + return ChannelMode::UNKNOWN; + } +} + +/** + * AAC Class implementation + */ + +const A2dpOffloadCodecAac* A2dpOffloadCodecAac::GetInstance() { + static A2dpOffloadCodecAac instance; + return &instance; +} + +A2dpOffloadCodecAac::A2dpOffloadCodecAac() + : A2dpOffloadCodec(info_), + info_({.id = CodecId(CodecId::A2dp::AAC), .name = "AAC"}) { + info_.transport.set(); + auto& a2dp_info = info_.transport.get(); + + /* --- Setup Capabilities --- */ + + a2dp_info.capabilities.resize(kCapabilitiesSize); + std::fill(begin(a2dp_info.capabilities), end(a2dp_info.capabilities), 0); + + auto capabilities = A2dpBits(a2dp_info.capabilities); + + capabilities.set(kObjectTypeMpeg2AacLc, kEnableObjectTypeMpeg2AacLc); + capabilities.set(kObjectTypeMpeg4AacLc, kEnableObjectTypeMpeg4AacLc); + + capabilities.set(kSamplingFrequency44100, kEnableSamplingFrequency44100); + capabilities.set(kSamplingFrequency48000, kEnableSamplingFrequency48000); + capabilities.set(kSamplingFrequency88200, kEnableSamplingFrequency88200); + capabilities.set(kSamplingFrequency96000, kEnableSamplingFrequency96000); + + capabilities.set(kChannels1, kEnableChannels1); + capabilities.set(kChannels2, kEnableChannels2); + + capabilities.set(kVbrSupported, kEnableVbrSupported); + + /* --- Setup Sampling Frequencies --- */ + + auto& sampling_frequency = a2dp_info.samplingFrequencyHz; + + for (auto v : {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 64000, 88200, 96000}) + if (capabilities.get(GetSamplingFrequencyBit(int32_t(v)))) + sampling_frequency.push_back(v); + + /* --- Setup Channel Modes --- */ + + auto& channel_modes = a2dp_info.channelMode; + + for (auto v : {ChannelMode::MONO, ChannelMode::STEREO}) + if (capabilities.get(GetChannelsBit(v))) channel_modes.push_back(v); + + /* --- Setup Bitdepth --- */ + + a2dp_info.bitdepth.push_back(kBitdepth); +} + +A2dpStatus A2dpOffloadCodecAac::ParseConfiguration( + const std::vector& configuration, + CodecParameters* codec_parameters, AacParameters* aac_parameters) const { + auto& a2dp_info = info.transport.get(); + + if (configuration.size() != a2dp_info.capabilities.size()) + return A2dpStatus::BAD_LENGTH; + + auto config = A2dpBits(configuration); + auto lcaps = A2dpBits(a2dp_info.capabilities); + + /* --- Check Object Type --- */ + + int object_type = config.find_active_bit(kObjectType); + if (object_type < 0) return A2dpStatus::INVALID_OBJECT_TYPE; + if (!lcaps.get(object_type)) return A2dpStatus::NOT_SUPPORTED_OBJECT_TYPE; + + /* --- Check Sampling Frequency --- */ + + int sampling_frequency = config.find_active_bit(kSamplingFrequency); + if (sampling_frequency < 0) return A2dpStatus::INVALID_SAMPLING_FREQUENCY; + if (!lcaps.get(sampling_frequency)) + return A2dpStatus::NOT_SUPPORTED_SAMPLING_FREQUENCY; + + /* --- Check Channels --- */ + + int channels = config.find_active_bit(kChannels); + if (channels < 0) return A2dpStatus::INVALID_CHANNELS; + if (!lcaps.get(channels)) return A2dpStatus::NOT_SUPPORTED_CHANNELS; + + /* --- Check Bitrate --- */ + + bool vbr = config.get(kVbrSupported); + if (vbr && !lcaps.get(kVbrSupported)) return A2dpStatus::NOT_SUPPORTED_VBR; + + int bitrate = config.get(kBitrate); + if (vbr && lcaps.get(kBitrate) && bitrate > lcaps.get(kBitrate)) + return A2dpStatus::NOT_SUPPORTED_BIT_RATE; + + /* --- Return --- */ + + codec_parameters->channelMode = GetChannelModeEnum(channels); + codec_parameters->samplingFrequencyHz = + GetSamplingFrequencyValue(sampling_frequency); + codec_parameters->bitdepth = kBitdepth; + + codec_parameters->minBitrate = vbr ? 0 : bitrate; + codec_parameters->maxBitrate = bitrate; + + if (aac_parameters) + aac_parameters->object_type = GetObjectTypeEnum(object_type); + + return A2dpStatus::OK; +} + +bool A2dpOffloadCodecAac::BuildConfiguration( + const std::vector& remote_capabilities, + const std::optional& hint, + std::vector* configuration) const { + auto& a2dp_info = info_.transport.get(); + + if (remote_capabilities.size() != a2dp_info.capabilities.size()) return false; + + auto lcaps = A2dpBits(a2dp_info.capabilities); + auto rcaps = A2dpBits(remote_capabilities); + + configuration->resize(a2dp_info.capabilities.size()); + std::fill(begin(*configuration), end(*configuration), 0); + auto config = A2dpBits(*configuration); + + /* --- Select Object Type --- */ + + if (lcaps.get(kObjectTypeMpeg2AacLc) && rcaps.get(kObjectTypeMpeg2AacLc)) + config.set(kObjectTypeMpeg2AacLc); + else if (lcaps.get(kObjectTypeMpeg4AacLc) && rcaps.get(kObjectTypeMpeg4AacLc)) + config.set(kObjectTypeMpeg4AacLc); + else + return false; + + /* --- Select Sampling Frequency --- */ + + auto sf_hint = hint ? GetSamplingFrequencyBit(hint->samplingFrequencyHz) : -1; + + if (sf_hint >= 0 && lcaps.get(sf_hint) && rcaps.get(sf_hint)) + config.set(sf_hint); + else if (lcaps.get(kSamplingFrequency96000) && + rcaps.get(kSamplingFrequency96000)) + config.set(kSamplingFrequency96000); + else if (lcaps.get(kSamplingFrequency88200) && + rcaps.get(kSamplingFrequency88200)) + config.set(kSamplingFrequency88200); + else if (lcaps.get(kSamplingFrequency48000) && + rcaps.get(kSamplingFrequency48000)) + config.set(kSamplingFrequency48000); + else if (lcaps.get(kSamplingFrequency44100) && + rcaps.get(kSamplingFrequency44100)) + config.set(kSamplingFrequency44100); + else + return false; + + /* --- Select Channels --- */ + + auto ch_hint = hint ? GetChannelsBit(hint->channelMode) : -1; + + if (ch_hint >= 0 && lcaps.get(ch_hint) && rcaps.get(ch_hint)) + config.set(ch_hint); + else if (lcaps.get(kChannels2) && rcaps.get(kChannels2)) + config.set(kChannels2); + else if (lcaps.get(kChannels1) && rcaps.get(kChannels1)) + config.set(kChannels1); + else + return false; + + /* --- Select Bitrate --- */ + + if (!hint || hint->minBitrate == 0) + config.set(kVbrSupported, + lcaps.get(kVbrSupported) && rcaps.get(kVbrSupported)); + + int32_t bitrate = lcaps.get(kBitrate); + if (hint && hint->maxBitrate > 0 && bitrate) + bitrate = std::min(hint->maxBitrate, bitrate); + else if (hint && hint->maxBitrate > 0) + bitrate = hint->maxBitrate; + config.set(kBitrate, bitrate); + + return true; +} + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.h b/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.h new file mode 100644 index 0000000000..eefa89b521 --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 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. + */ + +#pragma once + +#include "A2dpOffloadCodec.h" + +namespace aidl::android::hardware::bluetooth::audio { + +struct AacParameters : public CodecParameters { + enum class ObjectType { MPEG2_AAC_LC, MPEG4_AAC_LC }; + + ObjectType object_type; +}; + +class A2dpOffloadCodecAac : public A2dpOffloadCodec { + CodecInfo info_; + + A2dpOffloadCodecAac(); + + A2dpStatus ParseConfiguration(const std::vector& configuration, + CodecParameters* codec_parameters, + AacParameters* aac_parameters) const; + + public: + static const A2dpOffloadCodecAac* GetInstance(); + + A2dpStatus ParseConfiguration( + const std::vector& configuration, + CodecParameters* codec_parameters) const override { + return ParseConfiguration(configuration, codec_parameters, nullptr); + } + + A2dpStatus ParseConfiguration(const std::vector& configuration, + AacParameters* aac_parameters) const { + return ParseConfiguration(configuration, aac_parameters, aac_parameters); + } + + bool BuildConfiguration(const std::vector& remote_capabilities, + const std::optional& hint, + std::vector* configuration) const override; +}; + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.cpp b/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.cpp new file mode 100644 index 0000000000..73d8fb178f --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 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. + */ + +#include "A2dpOffloadCodecFactory.h" + +#include +#include + +#include "A2dpOffloadCodecAac.h" +#include "A2dpOffloadCodecSbc.h" + +namespace aidl::android::hardware::bluetooth::audio { + +/** + * Local Capabilities Configuration + */ + +enum : bool { + kEnableAac = true, + kEnableSbc = true, +}; + +/** + * Class implementation + */ + +const A2dpOffloadCodecFactory* A2dpOffloadCodecFactory::GetInstance() { + static A2dpOffloadCodecFactory instance; + return &instance; +} + +A2dpOffloadCodecFactory::A2dpOffloadCodecFactory() + : name("Offload"), codecs(ranked_codecs_) { + ranked_codecs_.reserve(kEnableAac + kEnableSbc); + + if (kEnableAac) ranked_codecs_.push_back(A2dpOffloadCodecAac::GetInstance()); + if (kEnableSbc) ranked_codecs_.push_back(A2dpOffloadCodecSbc::GetInstance()); +} + +const A2dpOffloadCodec* A2dpOffloadCodecFactory::GetCodec(CodecId id) const { + auto codec = std::find_if(begin(ranked_codecs_), end(ranked_codecs_), + [&](auto c) { return id == c->info.id; }); + + return codec != end(ranked_codecs_) ? *codec : nullptr; +} + +bool A2dpOffloadCodecFactory::GetConfiguration( + const std::vector& remote_capabilities, + const A2dpConfigurationHint& hint, A2dpConfiguration* configuration) const { + decltype(ranked_codecs_) codecs; + + codecs.reserve(ranked_codecs_.size()); + + auto hinted_codec = + std::find_if(begin(ranked_codecs_), end(ranked_codecs_), + [&](auto c) { return hint.codecId == c->info.id; }); + + if (hinted_codec != end(ranked_codecs_)) codecs.push_back(*hinted_codec); + + std::copy_if(begin(ranked_codecs_), end(ranked_codecs_), + std::back_inserter(codecs), + [&](auto c) { return c != *hinted_codec; }); + + for (auto codec : codecs) { + auto rc = + std::find_if(begin(remote_capabilities), end(remote_capabilities), + [&](auto& rc__) { return codec->info.id == rc__.id; }); + + if ((rc == end(remote_capabilities)) || + !codec->BuildConfiguration(rc->capabilities, hint.codecParameters, + &configuration->configuration)) + continue; + + configuration->id = codec->info.id; + A2dpStatus status = codec->ParseConfiguration(configuration->configuration, + &configuration->parameters); + assert(status == A2dpStatus::OK); + + configuration->remoteSeid = rc->seid; + + return true; + } + + return false; +} + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.h b/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.h new file mode 100644 index 0000000000..3fb5b1dc64 --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 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. + */ + +#pragma once + +#include "A2dpOffloadCodec.h" + +namespace aidl::android::hardware::bluetooth::audio { + +class A2dpOffloadCodecFactory { + std::vector ranked_codecs_; + + A2dpOffloadCodecFactory(); + + public: + const std::string name; + const std::vector& codecs; + + static const A2dpOffloadCodecFactory* GetInstance(); + + const A2dpOffloadCodec* GetCodec(CodecId id) const; + + bool GetConfiguration(const std::vector&, + const A2dpConfigurationHint& hint, + A2dpConfiguration* configuration) const; +}; + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.cpp b/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.cpp new file mode 100644 index 0000000000..36d8f729eb --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.cpp @@ -0,0 +1,510 @@ +/* + * Copyright (C) 2023 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. + */ + +#include "A2dpOffloadCodecSbc.h" + +#include + +#include "A2dpBits.h" + +namespace aidl::android::hardware::bluetooth::audio { + +/** + * SBC Local Capabilities + */ + +enum : bool { + kEnableSamplingFrequency44100 = true, + kEnableSamplingFrequency48000 = true, +}; + +enum : bool { + kEnableChannelModeMono = true, + kEnableChannelModeDualChannel = true, + kEnableChannelModeStereo = true, + kEnableChannelModeJointStereo = true, +}; + +enum : bool { + kEnableBlockLength4 = true, + kEnableBlockLength8 = true, + kEnableBlockLength12 = true, + kEnableBlockLength16 = true, +}; + +enum : bool { + kEnableSubbands4 = true, + kEnableSubbands8 = true, +}; + +enum : bool { + kEnableAllocationMethodSnr = true, + kEnableAllocationMethodLoudness = true, +}; + +enum : uint8_t { + kDefaultMinimumBitpool = 2, + kDefaultMaximumBitpool = 250, +}; + +enum : int { + kBitdepth = 16, +}; + +/** + * SBC Signaling format [A2DP - 4.3] + */ + +// clang-format off + +constexpr A2dpBits::Range kSamplingFrequency ( 0, 3 ); +constexpr A2dpBits::Range kChannelMode ( 4, 7 ); +constexpr A2dpBits::Range kBlockLength ( 8, 11 ); +constexpr A2dpBits::Range kSubbands ( 12, 13 ); +constexpr A2dpBits::Range kAllocationMethod ( 14, 15 ); +constexpr A2dpBits::Range kMinimumBitpool ( 16, 23 ); +constexpr A2dpBits::Range kMaximumBitpool ( 24, 31 ); +constexpr size_t kCapabilitiesSize = 32/8; + +// clang-format on + +enum { + kSamplingFrequency16000 = kSamplingFrequency.first, + kSamplingFrequency32000, + kSamplingFrequency44100, + kSamplingFrequency48000 +}; + +enum { + kChannelModeMono = kChannelMode.first, + kChannelModeDualChannel, + kChannelModeStereo, + kChannelModeJointStereo +}; + +enum { + kBlockLength4 = kBlockLength.first, + kBlockLength8, + kBlockLength12, + kBlockLength16 +}; + +enum { kSubbands8 = kSubbands.first, kSubbands4 }; + +enum { + kAllocationMethodSnr = kAllocationMethod.first, + kAllocationMethodLoudness +}; + +/** + * SBC Conversion functions + */ + +static int GetSamplingFrequencyBit(int32_t sampling_frequency) { + switch (sampling_frequency) { + case 16000: + return kSamplingFrequency16000; + case 32000: + return kSamplingFrequency32000; + case 44100: + return kSamplingFrequency44100; + case 48000: + return kSamplingFrequency48000; + default: + return -1; + } +} + +static int32_t GetSamplingFrequencyValue(int sampling_frequency) { + switch (sampling_frequency) { + case kSamplingFrequency16000: + return 16000; + case kSamplingFrequency32000: + return 32000; + case kSamplingFrequency44100: + return 44100; + case kSamplingFrequency48000: + return 48000; + default: + return 0; + } +} + +static int GetChannelModeBit(ChannelMode channel_mode) { + switch (channel_mode) { + case ChannelMode::STEREO: + return kChannelModeJointStereo | kChannelModeStereo; + case ChannelMode::DUALMONO: + return kChannelModeDualChannel; + case ChannelMode::MONO: + return kChannelModeMono; + default: + return -1; + } +} + +static ChannelMode GetChannelModeEnum(int channel_mode) { + switch (channel_mode) { + case kChannelModeMono: + return ChannelMode::MONO; + case kChannelModeDualChannel: + return ChannelMode::DUALMONO; + case kChannelModeStereo: + case kChannelModeJointStereo: + return ChannelMode::STEREO; + default: + return ChannelMode::UNKNOWN; + } +} + +static int32_t GetBlockLengthValue(int block_length) { + switch (block_length) { + case kBlockLength4: + return 4; + case kBlockLength8: + return 8; + case kBlockLength12: + return 12; + case kBlockLength16: + return 16; + default: + return 0; + } +} + +static int32_t GetSubbandsValue(int subbands) { + switch (subbands) { + case kSubbands4: + return 4; + case kSubbands8: + return 8; + default: + return 0; + } +} + +static SbcParameters::AllocationMethod GetAllocationMethodEnum( + int allocation_method) { + switch (allocation_method) { + case kAllocationMethodSnr: + return SbcParameters::AllocationMethod::SNR; + case kAllocationMethodLoudness: + default: + return SbcParameters::AllocationMethod::LOUDNESS; + } +} + +static int32_t GetSamplingFrequencyValue(const A2dpBits& configuration) { + return GetSamplingFrequencyValue( + configuration.find_active_bit(kSamplingFrequency)); +} + +static int32_t GetBlockLengthValue(const A2dpBits& configuration) { + return GetBlockLengthValue(configuration.find_active_bit(kBlockLength)); +} + +static int32_t GetSubbandsValue(const A2dpBits& configuration) { + return GetSubbandsValue(configuration.find_active_bit(kSubbands)); +} + +static int GetFrameSize(const A2dpBits& configuration, int bitpool) { + const int kSbcHeaderSize = 4; + int subbands = GetSubbandsValue(configuration); + int blocks = GetBlockLengthValue(configuration); + + unsigned bits = + ((4 * subbands) << !configuration.get(kChannelModeMono)) + + ((blocks * bitpool) << configuration.get(kChannelModeDualChannel)) + + ((configuration.get(kChannelModeJointStereo) ? subbands : 0)); + + return kSbcHeaderSize + ((bits + 7) >> 3); +} + +static int GetBitrate(const A2dpBits& configuration, int bitpool) { + int sampling_frequency = GetSamplingFrequencyValue(configuration); + int subbands = GetSubbandsValue(configuration); + int blocks = GetBlockLengthValue(configuration); + int bits = 8 * GetFrameSize(configuration, bitpool); + + return (bits * sampling_frequency) / (blocks * subbands); +} + +static uint8_t GetBitpool(const A2dpBits& configuration, int bitrate) { + int bitpool = 0; + + for (int i = 128; i; i >>= 1) + if (bitrate > GetBitrate(configuration, bitpool + i)) { + bitpool += i; + } + + return std::clamp(bitpool, 2, 250); +} + +/** + * SBC Class implementation + */ + +const A2dpOffloadCodecSbc* A2dpOffloadCodecSbc::GetInstance() { + static A2dpOffloadCodecSbc instance; + return &instance; +} + +A2dpOffloadCodecSbc::A2dpOffloadCodecSbc() + : A2dpOffloadCodec(info_), + info_({.id = CodecId(CodecId::A2dp::SBC), .name = "SBC"}) { + info_.transport.set(); + auto& a2dp_info = info_.transport.get(); + + /* --- Setup Capabilities --- */ + + a2dp_info.capabilities.resize(kCapabilitiesSize); + std::fill(begin(a2dp_info.capabilities), end(a2dp_info.capabilities), 0); + + auto capabilities = A2dpBits(a2dp_info.capabilities); + + capabilities.set(kSamplingFrequency44100, kEnableSamplingFrequency44100); + capabilities.set(kSamplingFrequency48000, kEnableSamplingFrequency48000); + + capabilities.set(kChannelModeMono, kEnableChannelModeMono); + capabilities.set(kChannelModeDualChannel, kEnableChannelModeDualChannel); + capabilities.set(kChannelModeStereo, kEnableChannelModeStereo); + capabilities.set(kChannelModeJointStereo, kEnableChannelModeJointStereo); + + capabilities.set(kBlockLength4, kEnableBlockLength4); + capabilities.set(kBlockLength8, kEnableBlockLength8); + capabilities.set(kBlockLength12, kEnableBlockLength12); + capabilities.set(kBlockLength16, kEnableBlockLength16); + + capabilities.set(kSubbands4, kEnableSubbands4); + capabilities.set(kSubbands8, kEnableSubbands8); + + capabilities.set(kSubbands4, kEnableSubbands4); + capabilities.set(kSubbands8, kEnableSubbands8); + + capabilities.set(kAllocationMethodSnr, kEnableAllocationMethodSnr); + capabilities.set(kAllocationMethodLoudness, kEnableAllocationMethodLoudness); + + capabilities.set(kMinimumBitpool, kDefaultMinimumBitpool); + capabilities.set(kMaximumBitpool, kDefaultMaximumBitpool); + + /* --- Setup Sampling Frequencies --- */ + + auto& sampling_frequency = a2dp_info.samplingFrequencyHz; + + for (auto v : {16000, 32000, 44100, 48000}) + if (capabilities.get(GetSamplingFrequencyBit(int32_t(v)))) + sampling_frequency.push_back(v); + + /* --- Setup Channel Modes --- */ + + auto& channel_modes = a2dp_info.channelMode; + + for (auto v : {ChannelMode::MONO, ChannelMode::DUALMONO, ChannelMode::STEREO}) + if (capabilities.get(GetChannelModeBit(v))) channel_modes.push_back(v); + + /* --- Setup Bitdepth --- */ + + a2dp_info.bitdepth.push_back(kBitdepth); +} + +A2dpStatus A2dpOffloadCodecSbc::ParseConfiguration( + const std::vector& configuration, + CodecParameters* codec_parameters, SbcParameters* sbc_parameters) const { + auto& a2dp_info = info.transport.get(); + + if (configuration.size() != a2dp_info.capabilities.size()) + return A2dpStatus::BAD_LENGTH; + + auto config = A2dpBits(configuration); + auto lcaps = A2dpBits(a2dp_info.capabilities); + + /* --- Check Sampling Frequency --- */ + + int sampling_frequency = config.find_active_bit(kSamplingFrequency); + if (sampling_frequency < 0) return A2dpStatus::INVALID_SAMPLING_FREQUENCY; + if (!lcaps.get(sampling_frequency)) + return A2dpStatus::NOT_SUPPORTED_SAMPLING_FREQUENCY; + + /* --- Check Channel Mode --- */ + + int channel_mode = config.find_active_bit(kChannelMode); + if (channel_mode < 0) return A2dpStatus::INVALID_CHANNEL_MODE; + if (!lcaps.get(channel_mode)) return A2dpStatus::NOT_SUPPORTED_CHANNEL_MODE; + + /* --- Check Block Length --- */ + + int block_length = config.find_active_bit(kBlockLength); + if (block_length < 0) return A2dpStatus::INVALID_BLOCK_LENGTH; + + /* --- Check Subbands --- */ + + int subbands = config.find_active_bit(kSubbands); + if (subbands < 0) return A2dpStatus::INVALID_SUBBANDS; + if (!lcaps.get(subbands)) return A2dpStatus::NOT_SUPPORTED_SUBBANDS; + + /* --- Check Allocation Method --- */ + + int allocation_method = config.find_active_bit(kAllocationMethod); + if (allocation_method < 0) return A2dpStatus::INVALID_ALLOCATION_METHOD; + if (!lcaps.get(allocation_method)) + return A2dpStatus::NOT_SUPPORTED_ALLOCATION_METHOD; + + /* --- Check Bitpool --- */ + + uint8_t min_bitpool = config.get(kMinimumBitpool); + if (min_bitpool < 2 || min_bitpool > 250) + return A2dpStatus::INVALID_MINIMUM_BITPOOL_VALUE; + if (min_bitpool < lcaps.get(kMinimumBitpool)) + return A2dpStatus::NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE; + + uint8_t max_bitpool = config.get(kMaximumBitpool); + if (max_bitpool < 2 || max_bitpool > 250) + return A2dpStatus::INVALID_MAXIMUM_BITPOOL_VALUE; + if (max_bitpool > lcaps.get(kMaximumBitpool)) + return A2dpStatus::NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE; + + /* --- Return --- */ + + codec_parameters->channelMode = GetChannelModeEnum(channel_mode); + codec_parameters->samplingFrequencyHz = + GetSamplingFrequencyValue(sampling_frequency); + codec_parameters->bitdepth = kBitdepth; + + codec_parameters->minBitrate = GetBitrate(config, min_bitpool); + codec_parameters->maxBitrate = GetBitrate(config, max_bitpool); + + if (sbc_parameters) { + sbc_parameters->block_length = GetBlockLengthValue(block_length); + sbc_parameters->subbands = GetSubbandsValue(subbands); + sbc_parameters->allocation_method = + GetAllocationMethodEnum(allocation_method); + sbc_parameters->min_bitpool = min_bitpool; + sbc_parameters->max_bitpool = max_bitpool; + } + + return A2dpStatus::OK; +} + +bool A2dpOffloadCodecSbc::BuildConfiguration( + const std::vector& remote_capabilities, + const std::optional& hint, + std::vector* configuration) const { + auto& a2dp_info = info.transport.get(); + + if (remote_capabilities.size() != a2dp_info.capabilities.size()) return false; + + auto lcaps = A2dpBits(a2dp_info.capabilities); + auto rcaps = A2dpBits(remote_capabilities); + + configuration->resize(a2dp_info.capabilities.size()); + std::fill(begin(*configuration), end(*configuration), 0); + auto config = A2dpBits(*configuration); + + /* --- Select Sampling Frequency --- */ + + auto sf_hint = hint ? GetSamplingFrequencyBit(hint->samplingFrequencyHz) : -1; + + if (sf_hint >= 0 && lcaps.get(sf_hint) && rcaps.get(sf_hint)) + config.set(sf_hint); + else if (lcaps.get(kSamplingFrequency44100) && + rcaps.get(kSamplingFrequency44100)) + config.set(kSamplingFrequency44100); + else if (lcaps.get(kSamplingFrequency48000) && + rcaps.get(kSamplingFrequency48000)) + config.set(kSamplingFrequency48000); + else + return false; + + /* --- Select Channel Mode --- */ + + auto cm_hint = hint ? GetChannelModeBit(hint->channelMode) : -1; + + if (cm_hint >= 0 && lcaps.get(cm_hint) && rcaps.get(cm_hint)) + config.set(cm_hint); + else if (lcaps.get(kChannelModeJointStereo) && + rcaps.get(kChannelModeJointStereo)) + config.set(kChannelModeJointStereo); + else if (lcaps.get(kChannelModeStereo) && rcaps.get(kChannelModeStereo)) + config.set(kChannelModeStereo); + else if (lcaps.get(kChannelModeDualChannel) && + rcaps.get(kChannelModeDualChannel)) + config.set(kChannelModeDualChannel); + else if (lcaps.get(kChannelModeMono) && rcaps.get(kChannelModeMono)) + config.set(kChannelModeMono); + else + return false; + + /* --- Select Block Length --- */ + + if (lcaps.get(kBlockLength16) && rcaps.get(kBlockLength16)) + config.set(kBlockLength16); + else if (lcaps.get(kBlockLength12) && rcaps.get(kBlockLength12)) + config.set(kBlockLength12); + else if (lcaps.get(kBlockLength8) && rcaps.get(kBlockLength8)) + config.set(kBlockLength8); + else if (lcaps.get(kBlockLength4) && rcaps.get(kBlockLength4)) + config.set(kBlockLength4); + else + return false; + + /* --- Select Subbands --- */ + + if (lcaps.get(kSubbands8) && rcaps.get(kSubbands8)) + config.set(kSubbands8); + else if (lcaps.get(kSubbands4) && rcaps.get(kSubbands4)) + config.set(kSubbands4); + else + return false; + + /* --- Select Allocation method --- */ + + if (lcaps.get(kAllocationMethodLoudness) && + rcaps.get(kAllocationMethodLoudness)) + config.set(kAllocationMethodLoudness); + else if (lcaps.get(kAllocationMethodSnr) && rcaps.get(kAllocationMethodSnr)) + config.set(kAllocationMethodSnr); + else + return false; + + /* --- Select Bitpool --- */ + + uint8_t min_bitpool = rcaps.get(kMinimumBitpool); + uint8_t max_bitpool = rcaps.get(kMaximumBitpool); + + if (min_bitpool < 2 || min_bitpool > 250 || max_bitpool < 2 || + max_bitpool > 250 || min_bitpool > max_bitpool) { + min_bitpool = 2; + max_bitpool = 250; + } + + min_bitpool = std::max(min_bitpool, uint8_t(lcaps.get(kMinimumBitpool))); + max_bitpool = std::max(max_bitpool, uint8_t(lcaps.get(kMaximumBitpool))); + + if (hint) { + min_bitpool = + std::max(min_bitpool, GetBitpool(*configuration, hint->minBitrate)); + if (hint->maxBitrate && hint->maxBitrate >= hint->minBitrate) + max_bitpool = + std::min(max_bitpool, GetBitpool(*configuration, hint->maxBitrate)); + } + + config.set(kMinimumBitpool, min_bitpool); + config.set(kMaximumBitpool, max_bitpool); + + return true; +} + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.h b/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.h new file mode 100644 index 0000000000..c380850cbb --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 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. + */ + +#pragma once + +#include "A2dpOffloadCodec.h" + +namespace aidl::android::hardware::bluetooth::audio { + +struct SbcParameters : public CodecParameters { + enum class AllocationMethod { SNR, LOUDNESS }; + + AllocationMethod allocation_method; + int block_length; + int subbands; + int min_bitpool; + int max_bitpool; +}; + +class A2dpOffloadCodecSbc : public A2dpOffloadCodec { + CodecInfo info_; + + A2dpOffloadCodecSbc(); + + A2dpStatus ParseConfiguration(const std::vector& configuration, + CodecParameters* codec_parameters, + SbcParameters* sbc_parameters) const; + + public: + static const A2dpOffloadCodecSbc* GetInstance(); + + A2dpStatus ParseConfiguration( + const std::vector& configuration, + CodecParameters* codec_parameters) const override { + return ParseConfiguration(configuration, codec_parameters, nullptr); + } + + A2dpStatus ParseConfiguration(const std::vector& configuration, + SbcParameters* sbc_parameters) const { + return ParseConfiguration(configuration, sbc_parameters, sbc_parameters); + } + + bool BuildConfiguration(const std::vector& remote_capabilities, + const std::optional& hint, + std::vector* configuration) const override; +}; + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/Android.bp b/bluetooth/audio/aidl/default/Android.bp index ad4d8ce4be..69db1b3e38 100644 --- a/bluetooth/audio/aidl/default/Android.bp +++ b/bluetooth/audio/aidl/default/Android.bp @@ -18,6 +18,9 @@ cc_library_shared { "BluetoothAudioProvider.cpp", "BluetoothAudioProviderFactory.cpp", "A2dpOffloadAudioProvider.cpp", + "A2dpOffloadCodecAac.cpp", + "A2dpOffloadCodecFactory.cpp", + "A2dpOffloadCodecSbc.cpp", "A2dpSoftwareAudioProvider.cpp", "HearingAidAudioProvider.cpp", "HfpOffloadAudioProvider.cpp", diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp index f9b18f8798..b88e66aea0 100644 --- a/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp @@ -21,6 +21,8 @@ #include #include +#include "A2dpOffloadCodecFactory.h" + namespace aidl { namespace android { namespace hardware { @@ -164,6 +166,27 @@ ndk::ScopedAStatus BluetoothAudioProvider::setLowLatencyModeAllowed( return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus BluetoothAudioProvider::parseA2dpConfiguration( + [[maybe_unused]] const CodecId& codec_id, + [[maybe_unused]] const std::vector& configuration, + [[maybe_unused]] CodecParameters* codec_parameters, + [[maybe_unused]] A2dpStatus* _aidl_return) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << " is illegal"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +} + +ndk::ScopedAStatus BluetoothAudioProvider::getA2dpConfiguration( + [[maybe_unused]] const std::vector& + remote_a2dp_capabilities, + [[maybe_unused]] const A2dpConfigurationHint& hint, + [[maybe_unused]] std::optional* _aidl_return) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << " is illegal"; + + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +} + } // namespace audio } // namespace bluetooth } // namespace hardware diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProvider.h b/bluetooth/audio/aidl/default/BluetoothAudioProvider.h index b6e07a1783..27ab13f50a 100644 --- a/bluetooth/audio/aidl/default/BluetoothAudioProvider.h +++ b/bluetooth/audio/aidl/default/BluetoothAudioProvider.h @@ -50,6 +50,14 @@ class BluetoothAudioProvider : public BnBluetoothAudioProvider { const AudioConfiguration& audio_config); ndk::ScopedAStatus setLowLatencyModeAllowed(bool allowed); + ndk::ScopedAStatus parseA2dpConfiguration( + const CodecId& codec_id, const std::vector& configuration, + CodecParameters* codec_parameters, A2dpStatus* _aidl_return); + ndk::ScopedAStatus getA2dpConfiguration( + const std::vector& remote_a2dp_capabilities, + const A2dpConfigurationHint& hint, + std::optional* _aidl_return); + virtual bool isValid(const SessionType& sessionType) = 0; protected: diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp index 8c6421d590..3a64c4d046 100644 --- a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp +++ b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp @@ -22,6 +22,7 @@ #include #include "A2dpOffloadAudioProvider.h" +#include "A2dpOffloadCodecFactory.h" #include "A2dpSoftwareAudioProvider.h" #include "BluetoothAudioProvider.h" #include "HearingAidAudioProvider.h" @@ -150,7 +151,14 @@ ndk::ScopedAStatus BluetoothAudioProviderFactory::getProviderInfo( SessionType session_type, std::optional* _aidl_return) { *_aidl_return = std::nullopt; - (void)session_type; + if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH || + session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) { + auto& provider_info = _aidl_return->emplace(); + + provider_info.name = A2dpOffloadCodecFactory::GetInstance()->name; + for (auto codec : A2dpOffloadCodecFactory::GetInstance()->codecs) + provider_info.codecInfos.push_back(codec->info); + } return ndk::ScopedAStatus::ok(); } diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp index c1a1b7af84..17be7be9d7 100644 --- a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp +++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp @@ -33,6 +33,11 @@ using aidl::android::hardware::audio::common::SinkMetadata; using aidl::android::hardware::audio::common::SourceMetadata; +using aidl::android::hardware::bluetooth::audio::A2dpConfiguration; +using aidl::android::hardware::bluetooth::audio::A2dpConfigurationHint; +using aidl::android::hardware::bluetooth::audio::A2dpRemoteCapabilities; +using aidl::android::hardware::bluetooth::audio::A2dpStatus; +using aidl::android::hardware::bluetooth::audio::A2dpStreamConfiguration; using aidl::android::hardware::bluetooth::audio::AacCapabilities; using aidl::android::hardware::bluetooth::audio::AacConfiguration; using aidl::android::hardware::bluetooth::audio::AptxAdaptiveLeCapabilities; @@ -48,6 +53,7 @@ using aidl::android::hardware::bluetooth::audio::CodecCapabilities; using aidl::android::hardware::bluetooth::audio::CodecConfiguration; using aidl::android::hardware::bluetooth::audio::CodecId; using aidl::android::hardware::bluetooth::audio::CodecInfo; +using aidl::android::hardware::bluetooth::audio::CodecParameters; using aidl::android::hardware::bluetooth::audio::CodecType; using aidl::android::hardware::bluetooth::audio::HfpConfiguration; using aidl::android::hardware::bluetooth::audio::IBluetoothAudioPort; @@ -707,6 +713,701 @@ TEST_P(BluetoothAudioProviderFactoryAidl, getProviderInfo_leAudioSessionTypes) { } } +class BluetoothAudioProviderAidl : public BluetoothAudioProviderFactoryAidl { + protected: + std::optional + a2dp_encoding_provider_info_{}; + std::optional + a2dp_decoding_provider_info_{}; + std::shared_ptr a2dp_encoding_provider_{nullptr}; + std::shared_ptr a2dp_decoding_provider_{nullptr}; + + public: + void SetUp() override { + BluetoothAudioProviderFactoryAidl::SetUp(); + audio_port_ = ndk::SharedRefBase::make(); + + (void)provider_factory_->getProviderInfo( + SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH, + &a2dp_encoding_provider_info_); + + (void)provider_factory_->getProviderInfo( + SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH, + &a2dp_decoding_provider_info_); + + (void)provider_factory_->openProvider( + SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH, + &a2dp_encoding_provider_); + + (void)provider_factory_->openProvider( + SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH, + &a2dp_decoding_provider_); + } +}; + +/** + * Calling parseA2dpConfiguration on a session of a different type than + * A2DP_HARDWARE_OFFLOAD_(ENCODING|DECODING)_DATAPATH must fail. + */ +TEST_P(BluetoothAudioProviderAidl, parseA2dpConfiguration_invalidSessionType) { + static constexpr SessionType kInvalidSessionTypes[] = { + SessionType::UNKNOWN, + SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH, + SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH, + SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH, + SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH, + SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH, + SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH, + SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH, + SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH, + SessionType::A2DP_SOFTWARE_DECODING_DATAPATH, + }; + + for (auto session_type : kInvalidSessionTypes) { + // Open a BluetoothAudioProvider instance of the selected session type. + // Skip validation if the provider cannot be opened. + std::shared_ptr provider{nullptr}; + (void)provider_factory_->openProvider(session_type, &provider); + if (provider == nullptr) { + continue; + } + + // parseA2dpConfiguration must fail without returning an A2dpStatus. + CodecId codec_id(CodecId::A2dp::SBC); + CodecParameters codec_parameters; + A2dpStatus a2dp_status = A2dpStatus::OK; + auto aidl_retval = provider->parseA2dpConfiguration( + codec_id, std::vector{}, &codec_parameters, &a2dp_status); + EXPECT_FALSE(aidl_retval.isOk()); + } +} + +/** + * Calling parseA2dpConfiguration with an unknown codec must fail + * with the A2dpStatus code INVALID_CODEC_TYPE or NOT_SUPPORTED_CODEC_TYPE. + */ +TEST_P(BluetoothAudioProviderAidl, + parseA2dpConfiguration_unsupportedCodecType) { + CodecId unsupported_core_id(CodecId::Core::CVSD); + CodecId unsupported_vendor_id( + CodecId::Vendor(0xFCB1, 0x42)); // Google Codec #42 + + for (auto& provider : {a2dp_encoding_provider_, a2dp_decoding_provider_}) { + if (provider == nullptr) { + continue; + } + + CodecParameters codec_parameters; + A2dpStatus a2dp_status = A2dpStatus::OK; + ::ndk::ScopedAStatus aidl_retval; + + // Test with two invalid codec identifiers: vendor or core. + aidl_retval = provider->parseA2dpConfiguration( + unsupported_core_id, std::vector{}, &codec_parameters, + &a2dp_status); + EXPECT_TRUE(!aidl_retval.isOk() || + a2dp_status == A2dpStatus::NOT_SUPPORTED_CODEC_TYPE); + + aidl_retval = provider->parseA2dpConfiguration( + unsupported_vendor_id, std::vector{}, &codec_parameters, + &a2dp_status); + EXPECT_TRUE(!aidl_retval.isOk() || + a2dp_status == A2dpStatus::NOT_SUPPORTED_CODEC_TYPE); + } +} + +/** + * Calling parseA2dpConfiguration with a known codec and invalid configuration + * must fail with an A2dpStatus code different from INVALID_CODEC_TYPE or + * NOT_SUPPORTED_CODEC_TYPE. + */ +TEST_P(BluetoothAudioProviderAidl, + parseA2dpConfiguration_invalidConfiguration) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + CodecParameters codec_parameters; + A2dpStatus a2dp_status = A2dpStatus::OK; + ::ndk::ScopedAStatus aidl_retval; + + // Test with the first available codec in the provider info for testing. + // The test runs with an empty parameters array, anything more specific + // would need understanding the codec. + aidl_retval = provider->parseA2dpConfiguration( + provider_info->codecInfos[0].id, std::vector{}, + &codec_parameters, &a2dp_status); + ASSERT_TRUE(aidl_retval.isOk()); + EXPECT_TRUE(a2dp_status != A2dpStatus::OK && + a2dp_status != A2dpStatus::NOT_SUPPORTED_CODEC_TYPE && + a2dp_status != A2dpStatus::INVALID_CODEC_TYPE); + } +} + +/** + * Calling parseA2dpConfiguration with a known codec and valid parameters + * must return with A2dpStatus OK. + */ +TEST_P(BluetoothAudioProviderAidl, parseA2dpConfiguration_valid) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + CodecParameters codec_parameters; + A2dpStatus a2dp_status = A2dpStatus::OK; + ::ndk::ScopedAStatus aidl_retval; + + // Test with the first available codec in the provider info for testing. + // To get a valid configuration (the capabilities array in the provider + // info is not a selection), getA2dpConfiguration is used with the + // selected codec parameters as input. + auto const& codec_info = provider_info->codecInfos[0]; + auto transport = codec_info.transport.get(); + A2dpRemoteCapabilities remote_capabilities(/*seid*/ 0, codec_info.id, + transport.capabilities); + std::optional configuration; + aidl_retval = provider->getA2dpConfiguration( + std::vector{remote_capabilities}, + A2dpConfigurationHint(), &configuration); + ASSERT_TRUE(aidl_retval.isOk()); + ASSERT_TRUE(configuration.has_value()); + + aidl_retval = provider->parseA2dpConfiguration( + configuration->id, configuration->configuration, &codec_parameters, + &a2dp_status); + ASSERT_TRUE(aidl_retval.isOk()); + EXPECT_TRUE(a2dp_status == A2dpStatus::OK); + EXPECT_EQ(codec_parameters, configuration->parameters); + } +} + +/** + * Calling getA2dpConfiguration on a session of a different type than + * A2DP_HARDWARE_OFFLOAD_(ENCODING|DECODING)_DATAPATH must fail. + */ +TEST_P(BluetoothAudioProviderAidl, getA2dpConfiguration_invalidSessionType) { + static constexpr SessionType kInvalidSessionTypes[] = { + SessionType::UNKNOWN, + SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH, + SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH, + SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH, + SessionType::LE_AUDIO_SOFTWARE_DECODING_DATAPATH, + SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH, + SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH, + SessionType::LE_AUDIO_BROADCAST_SOFTWARE_ENCODING_DATAPATH, + SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH, + SessionType::A2DP_SOFTWARE_DECODING_DATAPATH, + }; + + for (auto session_type : kInvalidSessionTypes) { + // Open a BluetoothAudioProvider instance of the selected session type. + // Skip validation if the provider cannot be opened. + std::shared_ptr provider{nullptr}; + auto aidl_retval = provider_factory_->openProvider(session_type, &provider); + if (provider == nullptr) { + continue; + } + + // getA2dpConfiguration must fail without returning a configuration. + std::optional configuration; + aidl_retval = + provider->getA2dpConfiguration(std::vector{}, + A2dpConfigurationHint(), &configuration); + EXPECT_FALSE(aidl_retval.isOk()); + } +} + +/** + * Calling getA2dpConfiguration with empty or unknown remote capabilities + * must return an empty configuration. + */ +TEST_P(BluetoothAudioProviderAidl, + getA2dpConfiguration_unknownRemoteCapabilities) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + std::optional configuration; + ::ndk::ScopedAStatus aidl_retval; + + // Test with empty remote capabilities. + aidl_retval = + provider->getA2dpConfiguration(std::vector{}, + A2dpConfigurationHint(), &configuration); + ASSERT_TRUE(aidl_retval.isOk()); + EXPECT_FALSE(configuration.has_value()); + + // Test with unknown remote capabilities. + A2dpRemoteCapabilities unknown_core_remote_capabilities( + /*seid*/ 0, CodecId::Core::CVSD, std::vector{1, 2, 3}); + A2dpRemoteCapabilities unknown_vendor_remote_capabilities( + /*seid*/ 1, + /* Google Codec #42 */ CodecId::Vendor(0xFCB1, 0x42), + std::vector{1, 2, 3}); + aidl_retval = provider->getA2dpConfiguration( + std::vector{ + unknown_core_remote_capabilities, + unknown_vendor_remote_capabilities, + }, + A2dpConfigurationHint(), &configuration); + ASSERT_TRUE(aidl_retval.isOk()); + EXPECT_FALSE(configuration.has_value()); + } +} + +/** + * Calling getA2dpConfiguration with invalid remote capabilities + * must return an empty configuration. + */ +TEST_P(BluetoothAudioProviderAidl, + getA2dpConfiguration_invalidRemoteCapabilities) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + std::optional configuration; + ::ndk::ScopedAStatus aidl_retval; + + // Use the first available codec in the provider info for testing. + // The capabilities are modified to make them invalid. + auto const& codec_info = provider_info->codecInfos[0]; + auto transport = codec_info.transport.get(); + std::vector invalid_capabilities = transport.capabilities; + invalid_capabilities.push_back(0x42); // adding bytes should be invalid. + aidl_retval = provider->getA2dpConfiguration( + std::vector{ + A2dpRemoteCapabilities(/*seid*/ 0, codec_info.id, + std::vector()), + A2dpRemoteCapabilities(/*seid*/ 1, codec_info.id, + invalid_capabilities), + }, + A2dpConfigurationHint(), &configuration); + ASSERT_TRUE(aidl_retval.isOk()); + EXPECT_FALSE(configuration.has_value()); + } +} + +/** + * Calling getA2dpConfiguration with valid remote capabilities + * must return a valid configuration. The selected parameters must + * be contained in the original capabilities. The returned configuration + * must match the returned parameters. The returned SEID must match the + * input SEID. + */ +TEST_P(BluetoothAudioProviderAidl, + getA2dpConfiguration_validRemoteCapabilities) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + // Test with all available codecs in the provider info. + for (auto const& codec_info : provider_info->codecInfos) { + auto a2dp_info = codec_info.transport.get(); + std::optional configuration{}; + ::ndk::ScopedAStatus aidl_retval; + + aidl_retval = provider->getA2dpConfiguration( + std::vector{ + A2dpRemoteCapabilities(/*seid*/ 42, codec_info.id, + a2dp_info.capabilities), + }, + A2dpConfigurationHint(), &configuration); + + ASSERT_TRUE(aidl_retval.isOk()); + ASSERT_TRUE(configuration.has_value()); + + // Returned configuration must have the same codec id + // as the remote capability. + EXPECT_EQ(configuration->id, codec_info.id); + + // Returned configuration must have the same SEID + // as the remote capability. + EXPECT_EQ(configuration->remoteSeid, 42); + + // Returned codec parameters must be in the range of input + // parameters. + EXPECT_NE( + std::find(a2dp_info.channelMode.begin(), a2dp_info.channelMode.end(), + configuration->parameters.channelMode), + a2dp_info.channelMode.end()); + EXPECT_NE(std::find(a2dp_info.samplingFrequencyHz.begin(), + a2dp_info.samplingFrequencyHz.end(), + configuration->parameters.samplingFrequencyHz), + a2dp_info.samplingFrequencyHz.end()); + EXPECT_NE(std::find(a2dp_info.bitdepth.begin(), a2dp_info.bitdepth.end(), + configuration->parameters.bitdepth), + a2dp_info.bitdepth.end()); + EXPECT_EQ(a2dp_info.lossless, configuration->parameters.lossless); + EXPECT_TRUE(configuration->parameters.minBitrate <= + configuration->parameters.maxBitrate); + + // Returned configuration must be parsable by parseA2dpParameters + // and match the codec parameters. + CodecParameters codec_parameters; + A2dpStatus a2dp_status = A2dpStatus::OK; + aidl_retval = provider->parseA2dpConfiguration( + configuration->id, configuration->configuration, &codec_parameters, + &a2dp_status); + ASSERT_TRUE(aidl_retval.isOk()); + EXPECT_TRUE(a2dp_status == A2dpStatus::OK); + EXPECT_EQ(codec_parameters, configuration->parameters); + } + } +} + +/** + * Calling getA2dpConfiguration with valid remote capabilities + * with various hinted codec ids. + */ +TEST_P(BluetoothAudioProviderAidl, getA2dpConfiguration_hintCodecId) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + // Build the remote capabilities with all supported codecs. + std::vector remote_capabilities; + for (size_t n = 0; n < provider_info->codecInfos.size(); n++) { + auto const& codec_info = provider_info->codecInfos[n]; + auto a2dp_info = codec_info.transport.get(); + remote_capabilities.push_back(A2dpRemoteCapabilities( + /*seid*/ n, codec_info.id, a2dp_info.capabilities)); + } + + // Test with all supported codec identifiers, + for (auto const& codec_info : provider_info->codecInfos) { + std::optional configuration{}; + ::ndk::ScopedAStatus aidl_retval; + + A2dpConfigurationHint hint; + hint.codecId = codec_info.id; + + aidl_retval = provider->getA2dpConfiguration(remote_capabilities, hint, + &configuration); + + ASSERT_TRUE(aidl_retval.isOk()); + ASSERT_TRUE(configuration.has_value()); + EXPECT_EQ(configuration->id, codec_info.id); + } + + // Test with unknown codec identifiers: either core or vendor. + for (auto& codec_id : + {CodecId(CodecId::Core::CVSD), + CodecId(CodecId::Vendor(0xFCB1, 0x42)) /*Google Codec #42*/}) { + std::optional configuration{}; + ::ndk::ScopedAStatus aidl_retval; + + A2dpConfigurationHint hint; + hint.codecId = codec_id; + + aidl_retval = provider->getA2dpConfiguration(remote_capabilities, hint, + &configuration); + + ASSERT_TRUE(aidl_retval.isOk()); + ASSERT_TRUE(configuration.has_value()); + EXPECT_NE(configuration->id, codec_id); + } + } +} + +/** + * Calling getA2dpConfiguration with valid remote capabilities + * with various hinted channel modes. + */ +TEST_P(BluetoothAudioProviderAidl, getA2dpConfiguration_hintChannelMode) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + // Test with all available codecs in the provider info. + for (auto const& codec_info : provider_info->codecInfos) { + auto a2dp_info = codec_info.transport.get(); + std::optional configuration{}; + ::ndk::ScopedAStatus aidl_retval; + + for (auto& channel_mode : + {ChannelMode::STEREO, ChannelMode::MONO, ChannelMode::DUALMONO}) { + // Add the hint for the channel mode. + A2dpConfigurationHint hint; + auto& codec_parameters = hint.codecParameters.emplace(); + codec_parameters.channelMode = channel_mode; + + aidl_retval = provider->getA2dpConfiguration( + std::vector{ + A2dpRemoteCapabilities(/*seid*/ 42, codec_info.id, + a2dp_info.capabilities), + }, + hint, &configuration); + + ASSERT_TRUE(aidl_retval.isOk()); + ASSERT_TRUE(configuration.has_value()); + + // The hint must be ignored if the channel mode is not supported + // by the codec, and applied otherwise. + ASSERT_EQ(configuration->parameters.channelMode == channel_mode, + std::find(a2dp_info.channelMode.begin(), + a2dp_info.channelMode.end(), + channel_mode) != a2dp_info.channelMode.end()); + } + } + } +} + +/** + * Calling getA2dpConfiguration with valid remote capabilities + * with various hinted sampling frequencies. + */ +TEST_P(BluetoothAudioProviderAidl, + getA2dpConfiguration_hintSamplingFrequencyHz) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + // Test with all available codecs in the provider info. + for (auto const& codec_info : provider_info->codecInfos) { + auto a2dp_info = codec_info.transport.get(); + std::optional configuration{}; + ::ndk::ScopedAStatus aidl_retval; + + for (auto& sampling_frequency_hz : { + 0, + 1, + 8000, + 16000, + 24000, + 32000, + 44100, + 48000, + 88200, + 96000, + 176400, + 192000, + }) { + // Add the hint for the sampling frequency. + A2dpConfigurationHint hint; + auto& codec_parameters = hint.codecParameters.emplace(); + codec_parameters.samplingFrequencyHz = sampling_frequency_hz; + + aidl_retval = provider->getA2dpConfiguration( + std::vector{ + A2dpRemoteCapabilities(/*seid*/ 42, codec_info.id, + a2dp_info.capabilities), + }, + hint, &configuration); + + ASSERT_TRUE(aidl_retval.isOk()); + ASSERT_TRUE(configuration.has_value()); + + // The hint must be ignored if the sampling frequency is not supported + // by the codec, and applied otherwise. + ASSERT_EQ(configuration->parameters.samplingFrequencyHz == + sampling_frequency_hz, + std::find(a2dp_info.samplingFrequencyHz.begin(), + a2dp_info.samplingFrequencyHz.end(), + sampling_frequency_hz) != + a2dp_info.samplingFrequencyHz.end()); + } + } + } +} + +/** + * Calling getA2dpConfiguration with valid remote capabilities + * with various hinted sampling bit-depths. + */ +TEST_P(BluetoothAudioProviderAidl, getA2dpConfiguration_hintBitdepth) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + // Test with all available codecs in the provider info. + for (auto const& codec_info : provider_info->codecInfos) { + auto a2dp_info = codec_info.transport.get(); + std::optional configuration{}; + ::ndk::ScopedAStatus aidl_retval; + + for (auto& bitdepth : {0, 1, 16, 24, 32}) { + // Add the hint for the bit depth. + A2dpConfigurationHint hint; + auto& codec_parameters = hint.codecParameters.emplace(); + codec_parameters.bitdepth = bitdepth; + + aidl_retval = provider->getA2dpConfiguration( + std::vector{ + A2dpRemoteCapabilities(/*seid*/ 42, codec_info.id, + a2dp_info.capabilities), + }, + hint, &configuration); + + ASSERT_TRUE(aidl_retval.isOk()); + ASSERT_TRUE(configuration.has_value()); + + // The hint must be ignored if the bitdepth is not supported + // by the codec, and applied otherwise. + ASSERT_EQ( + configuration->parameters.bitdepth == bitdepth, + std::find(a2dp_info.bitdepth.begin(), a2dp_info.bitdepth.end(), + bitdepth) != a2dp_info.bitdepth.end()); + } + } + } +} + +/** + * Calling startSession with an unknown codec id must fail. + */ +TEST_P(BluetoothAudioProviderAidl, startSession_unknownCodecId) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + for (auto& codec_id : + {CodecId(CodecId::Core::CVSD), + CodecId(CodecId::Vendor(0xFCB1, 0x42) /*Google Codec #42*/)}) { + A2dpStreamConfiguration a2dp_config; + DataMQDesc data_mq_desc; + + a2dp_config.codecId = codec_id; + a2dp_config.configuration = std::vector{1, 2, 3}; + + auto aidl_retval = + provider->startSession(audio_port_, AudioConfiguration(a2dp_config), + std::vector{}, &data_mq_desc); + + EXPECT_FALSE(aidl_retval.isOk()); + } + } +} + +/** + * Calling startSession with a known codec and a valid configuration + * must succeed. + */ +TEST_P(BluetoothAudioProviderAidl, startSession_valid) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + // Use the first available codec in the provider info for testing. + // To get a valid configuration (the capabilities array in the provider + // info is not a selection), getA2dpConfiguration is used with the + // selected codec parameters as input. + auto const& codec_info = provider_info->codecInfos[0]; + auto a2dp_info = codec_info.transport.get(); + ::ndk::ScopedAStatus aidl_retval; + A2dpRemoteCapabilities remote_capabilities(/*seid*/ 0, codec_info.id, + a2dp_info.capabilities); + std::optional configuration; + aidl_retval = provider->getA2dpConfiguration( + std::vector{remote_capabilities}, + A2dpConfigurationHint(), &configuration); + ASSERT_TRUE(aidl_retval.isOk()); + ASSERT_TRUE(configuration.has_value()); + + // Build the stream configuration. + A2dpStreamConfiguration a2dp_config; + DataMQDesc data_mq_desc; + + a2dp_config.codecId = codec_info.id; + a2dp_config.configuration = configuration->configuration; + + aidl_retval = + provider->startSession(audio_port_, AudioConfiguration(a2dp_config), + std::vector{}, &data_mq_desc); + + EXPECT_TRUE(aidl_retval.isOk()); + } +} + +/** + * Calling startSession with a known codec but an invalid configuration + * must fail. + */ +TEST_P(BluetoothAudioProviderAidl, startSession_invalidConfiguration) { + for (auto& [provider, provider_info] : + {std::pair(a2dp_encoding_provider_, a2dp_encoding_provider_info_), + std::pair(a2dp_decoding_provider_, a2dp_decoding_provider_info_)}) { + if (provider == nullptr || !provider_info.has_value() || + provider_info->codecInfos.empty()) { + continue; + } + + // Use the first available codec in the provider info for testing. + // To get a valid configuration (the capabilities array in the provider + // info is not a selection), getA2dpConfiguration is used with the + // selected codec parameters as input. + ::ndk::ScopedAStatus aidl_retval; + auto const& codec_info = provider_info->codecInfos[0]; + auto a2dp_info = codec_info.transport.get(); + A2dpRemoteCapabilities remote_capabilities(/*seid*/ 0, codec_info.id, + a2dp_info.capabilities); + std::optional configuration; + aidl_retval = provider->getA2dpConfiguration( + std::vector{remote_capabilities}, + A2dpConfigurationHint(), &configuration); + ASSERT_TRUE(aidl_retval.isOk()); + ASSERT_TRUE(configuration.has_value()); + + // Build the stream configuration but edit the configuration bytes + // to make it invalid. + A2dpStreamConfiguration a2dp_config; + DataMQDesc data_mq_desc; + + a2dp_config.codecId = codec_info.id; + a2dp_config.configuration = configuration->configuration; + a2dp_config.configuration.push_back(42); + + aidl_retval = + provider->startSession(audio_port_, AudioConfiguration(a2dp_config), + std::vector{}, &data_mq_desc); + + EXPECT_FALSE(aidl_retval.isOk()); + } +} + /** * openProvider A2DP_SOFTWARE_ENCODING_DATAPATH */ @@ -2393,6 +3094,12 @@ INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothAudioProviderFactoryAidl, IBluetoothAudioProviderFactory::descriptor)), android::PrintInstanceNameToString); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BluetoothAudioProviderAidl); +INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothAudioProviderAidl, + testing::ValuesIn(android::getAidlHalInstanceNames( + IBluetoothAudioProviderFactory::descriptor)), + android::PrintInstanceNameToString); + GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( BluetoothAudioProviderA2dpEncodingSoftwareAidl); INSTANTIATE_TEST_SUITE_P(PerInstance, diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp index 9dcfc13893..3519ace1f9 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp +++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp @@ -304,7 +304,8 @@ bool BluetoothAudioSession::UpdateAudioConfig( audio_config_tag == AudioConfiguration::pcmConfig); bool is_a2dp_offload_audio_config = (is_offload_a2dp_session && - audio_config_tag == AudioConfiguration::a2dpConfig); + (audio_config_tag == AudioConfiguration::a2dp || + audio_config_tag == AudioConfiguration::a2dpConfig)); bool is_hfp_offload_audio_config = (is_offload_hfp_session && audio_config_tag == AudioConfiguration::hfpConfig); diff --git a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp index a2a5bcb51e..fd12a6e0a6 100644 --- a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp +++ b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp @@ -467,6 +467,8 @@ inline AudioConfig_2_1 to_hidl_audio_config_2_1( hidl_audio_config.codecConfig(to_hidl_codec_config_2_0( audio_config.get())); break; + case AudioConfiguration::a2dp: + break; case AudioConfiguration::leAudioConfig: hidl_audio_config.leAudioCodecConfig(to_hidl_leaudio_config_2_1( audio_config.get()));