Merge changes from topic "codec_extensibility" into main
* changes: Codec Extensibility A2DP HAL Reference Implementation Codec Extensibility A2DP Specific AIDL
This commit is contained in:
commit
8024b94e6d
33 changed files with 2701 additions and 11 deletions
|
@ -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 <name>-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;
|
||||||
|
}
|
|
@ -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 <name>-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;
|
||||||
|
}
|
|
@ -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 <name>-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;
|
||||||
|
}
|
|
@ -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 <name>-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,
|
||||||
|
}
|
|
@ -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 <name>-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;
|
||||||
|
}
|
|
@ -39,4 +39,5 @@ union AudioConfiguration {
|
||||||
android.hardware.bluetooth.audio.LeAudioConfiguration leAudioConfig;
|
android.hardware.bluetooth.audio.LeAudioConfiguration leAudioConfig;
|
||||||
android.hardware.bluetooth.audio.LeAudioBroadcastConfiguration leAudioBroadcastConfig;
|
android.hardware.bluetooth.audio.LeAudioBroadcastConfiguration leAudioBroadcastConfig;
|
||||||
android.hardware.bluetooth.audio.HfpConfiguration hfpConfig;
|
android.hardware.bluetooth.audio.HfpConfiguration hfpConfig;
|
||||||
|
android.hardware.bluetooth.audio.A2dpStreamConfiguration a2dp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <name>-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;
|
||||||
|
}
|
|
@ -40,4 +40,6 @@ interface IBluetoothAudioProvider {
|
||||||
void streamSuspended(in android.hardware.bluetooth.audio.BluetoothAudioStatus status);
|
void streamSuspended(in android.hardware.bluetooth.audio.BluetoothAudioStatus status);
|
||||||
void updateAudioConfiguration(in android.hardware.bluetooth.audio.AudioConfiguration audioConfig);
|
void updateAudioConfiguration(in android.hardware.bluetooth.audio.AudioConfiguration audioConfig);
|
||||||
void setLowLatencyModeAllowed(in boolean allowed);
|
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<android.hardware.bluetooth.audio.A2dpRemoteCapabilities> remoteA2dpCapabilities, in android.hardware.bluetooth.audio.A2dpConfigurationHint hint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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,
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package android.hardware.bluetooth.audio;
|
package android.hardware.bluetooth.audio;
|
||||||
|
|
||||||
|
import android.hardware.bluetooth.audio.A2dpStreamConfiguration;
|
||||||
import android.hardware.bluetooth.audio.CodecConfiguration;
|
import android.hardware.bluetooth.audio.CodecConfiguration;
|
||||||
import android.hardware.bluetooth.audio.HfpConfiguration;
|
import android.hardware.bluetooth.audio.HfpConfiguration;
|
||||||
import android.hardware.bluetooth.audio.LeAudioBroadcastConfiguration;
|
import android.hardware.bluetooth.audio.LeAudioBroadcastConfiguration;
|
||||||
|
@ -32,4 +33,5 @@ union AudioConfiguration {
|
||||||
LeAudioConfiguration leAudioConfig;
|
LeAudioConfiguration leAudioConfig;
|
||||||
LeAudioBroadcastConfiguration leAudioBroadcastConfig;
|
LeAudioBroadcastConfiguration leAudioBroadcastConfig;
|
||||||
HfpConfiguration hfpConfig;
|
HfpConfiguration hfpConfig;
|
||||||
|
A2dpStreamConfiguration a2dp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -16,8 +16,14 @@
|
||||||
|
|
||||||
package android.hardware.bluetooth.audio;
|
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.AudioConfiguration;
|
||||||
import android.hardware.bluetooth.audio.BluetoothAudioStatus;
|
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.IBluetoothAudioPort;
|
||||||
import android.hardware.bluetooth.audio.LatencyMode;
|
import android.hardware.bluetooth.audio.LatencyMode;
|
||||||
import android.hardware.common.fmq.MQDescriptor;
|
import android.hardware.common.fmq.MQDescriptor;
|
||||||
|
@ -92,4 +98,30 @@ interface IBluetoothAudioProvider {
|
||||||
* mode, the API will be called with supported is false.
|
* mode, the API will be called with supported is false.
|
||||||
*/
|
*/
|
||||||
void setLowLatencyModeAllowed(in boolean allowed);
|
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<A2dpRemoteCapabilities> remoteA2dpCapabilities, in A2dpConfigurationHint hint);
|
||||||
}
|
}
|
||||||
|
|
73
bluetooth/audio/aidl/default/A2dpBits.h
Normal file
73
bluetooth/audio/aidl/default/A2dpBits.h
Normal file
|
@ -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<uint8_t>& vector) : cdata_(vector.data()) {}
|
||||||
|
|
||||||
|
A2dpBits(std::vector<uint8_t>& 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
|
|
@ -22,6 +22,10 @@
|
||||||
#include <BluetoothAudioSessionReport.h>
|
#include <BluetoothAudioSessionReport.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
|
#include "A2dpOffloadCodecAac.h"
|
||||||
|
#include "A2dpOffloadCodecFactory.h"
|
||||||
|
#include "A2dpOffloadCodecSbc.h"
|
||||||
|
|
||||||
namespace aidl {
|
namespace aidl {
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace hardware {
|
namespace hardware {
|
||||||
|
@ -48,19 +52,44 @@ ndk::ScopedAStatus A2dpOffloadAudioProvider::startSession(
|
||||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||||
const AudioConfiguration& audio_config,
|
const AudioConfiguration& audio_config,
|
||||||
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
|
||||||
if (audio_config.getTag() != AudioConfiguration::a2dpConfig) {
|
if (audio_config.getTag() == AudioConfiguration::Tag::a2dp) {
|
||||||
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
auto a2dp_config = audio_config.get<AudioConfiguration::Tag::a2dp>();
|
||||||
<< audio_config.toString();
|
A2dpStatus a2dp_status = A2dpStatus::NOT_SUPPORTED_CODEC_TYPE;
|
||||||
*_aidl_return = DataMQDesc();
|
|
||||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
if (a2dp_config.codecId ==
|
||||||
}
|
A2dpOffloadCodecSbc::GetInstance()->GetCodecId()) {
|
||||||
if (!BluetoothAudioCodecs::IsOffloadCodecConfigurationValid(
|
SbcParameters sbc_parameters;
|
||||||
session_type_, audio_config.get<AudioConfiguration::a2dpConfig>())) {
|
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<AudioConfiguration::a2dpConfig>())) {
|
||||||
|
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="
|
LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
|
||||||
<< audio_config.toString();
|
<< audio_config.toString();
|
||||||
*_aidl_return = DataMQDesc();
|
*_aidl_return = DataMQDesc();
|
||||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return BluetoothAudioProvider::startSession(
|
return BluetoothAudioProvider::startSession(
|
||||||
host_if, audio_config, latency_modes, _aidl_return);
|
host_if, audio_config, latency_modes, _aidl_return);
|
||||||
}
|
}
|
||||||
|
@ -73,6 +102,36 @@ ndk::ScopedAStatus A2dpOffloadAudioProvider::onSessionReady(
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus A2dpOffloadAudioProvider::parseA2dpConfiguration(
|
||||||
|
const CodecId& codec_id, const std::vector<uint8_t>& 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<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
|
||||||
|
const A2dpConfigurationHint& hint,
|
||||||
|
std::optional<audio::A2dpConfiguration>* _aidl_return) {
|
||||||
|
*_aidl_return = std::nullopt;
|
||||||
|
A2dpConfiguration avdtp_configuration;
|
||||||
|
|
||||||
|
if (A2dpOffloadCodecFactory::GetInstance()->GetConfiguration(
|
||||||
|
remote_a2dp_capabilities, hint, &avdtp_configuration))
|
||||||
|
*_aidl_return =
|
||||||
|
std::make_optional<A2dpConfiguration>(std::move(avdtp_configuration));
|
||||||
|
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace audio
|
} // namespace audio
|
||||||
} // namespace bluetooth
|
} // namespace bluetooth
|
||||||
} // namespace hardware
|
} // namespace hardware
|
||||||
|
|
|
@ -34,7 +34,16 @@ class A2dpOffloadAudioProvider : public BluetoothAudioProvider {
|
||||||
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
const std::shared_ptr<IBluetoothAudioPort>& host_if,
|
||||||
const AudioConfiguration& audio_config,
|
const AudioConfiguration& audio_config,
|
||||||
const std::vector<LatencyMode>& latency_modes,
|
const std::vector<LatencyMode>& latency_modes,
|
||||||
DataMQDesc* _aidl_return);
|
DataMQDesc* _aidl_return) override;
|
||||||
|
|
||||||
|
ndk::ScopedAStatus parseA2dpConfiguration(
|
||||||
|
const CodecId& codec_id, const std::vector<uint8_t>& configuration,
|
||||||
|
CodecParameters* codec_parameters, A2dpStatus* _aidl_return) override;
|
||||||
|
|
||||||
|
ndk::ScopedAStatus getA2dpConfiguration(
|
||||||
|
const std::vector<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
|
||||||
|
const A2dpConfigurationHint& hint,
|
||||||
|
std::optional<audio::A2dpConfiguration>* _aidl_return) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override;
|
||||||
|
|
47
bluetooth/audio/aidl/default/A2dpOffloadCodec.h
Normal file
47
bluetooth/audio/aidl/default/A2dpOffloadCodec.h
Normal file
|
@ -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 <aidl/android/hardware/bluetooth/audio/A2dpStatus.h>
|
||||||
|
#include <aidl/android/hardware/bluetooth/audio/ChannelMode.h>
|
||||||
|
#include <aidl/android/hardware/bluetooth/audio/CodecParameters.h>
|
||||||
|
|
||||||
|
#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<uint8_t>& configuration,
|
||||||
|
CodecParameters* codec_parameters) const = 0;
|
||||||
|
|
||||||
|
virtual bool BuildConfiguration(
|
||||||
|
const std::vector<uint8_t>& remote_capabilities,
|
||||||
|
const std::optional<CodecParameters>& hint,
|
||||||
|
std::vector<uint8_t>* configuration) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::bluetooth::audio
|
378
bluetooth/audio/aidl/default/A2dpOffloadCodecAac.cpp
Normal file
378
bluetooth/audio/aidl/default/A2dpOffloadCodecAac.cpp
Normal file
|
@ -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<CodecInfo::Transport::Tag::a2dp>();
|
||||||
|
auto& a2dp_info = info_.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||||
|
|
||||||
|
/* --- 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<uint8_t>& configuration,
|
||||||
|
CodecParameters* codec_parameters, AacParameters* aac_parameters) const {
|
||||||
|
auto& a2dp_info = info.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||||
|
|
||||||
|
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<uint8_t>& remote_capabilities,
|
||||||
|
const std::optional<CodecParameters>& hint,
|
||||||
|
std::vector<uint8_t>* configuration) const {
|
||||||
|
auto& a2dp_info = info_.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||||
|
|
||||||
|
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
|
57
bluetooth/audio/aidl/default/A2dpOffloadCodecAac.h
Normal file
57
bluetooth/audio/aidl/default/A2dpOffloadCodecAac.h
Normal file
|
@ -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<uint8_t>& configuration,
|
||||||
|
CodecParameters* codec_parameters,
|
||||||
|
AacParameters* aac_parameters) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const A2dpOffloadCodecAac* GetInstance();
|
||||||
|
|
||||||
|
A2dpStatus ParseConfiguration(
|
||||||
|
const std::vector<uint8_t>& configuration,
|
||||||
|
CodecParameters* codec_parameters) const override {
|
||||||
|
return ParseConfiguration(configuration, codec_parameters, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
A2dpStatus ParseConfiguration(const std::vector<uint8_t>& configuration,
|
||||||
|
AacParameters* aac_parameters) const {
|
||||||
|
return ParseConfiguration(configuration, aac_parameters, aac_parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuildConfiguration(const std::vector<uint8_t>& remote_capabilities,
|
||||||
|
const std::optional<CodecParameters>& hint,
|
||||||
|
std::vector<uint8_t>* configuration) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::bluetooth::audio
|
100
bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.cpp
Normal file
100
bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.cpp
Normal file
|
@ -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 <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#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<A2dpRemoteCapabilities>& 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
|
41
bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.h
Normal file
41
bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.h
Normal file
|
@ -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<const A2dpOffloadCodec*> ranked_codecs_;
|
||||||
|
|
||||||
|
A2dpOffloadCodecFactory();
|
||||||
|
|
||||||
|
public:
|
||||||
|
const std::string name;
|
||||||
|
const std::vector<const A2dpOffloadCodec*>& codecs;
|
||||||
|
|
||||||
|
static const A2dpOffloadCodecFactory* GetInstance();
|
||||||
|
|
||||||
|
const A2dpOffloadCodec* GetCodec(CodecId id) const;
|
||||||
|
|
||||||
|
bool GetConfiguration(const std::vector<A2dpRemoteCapabilities>&,
|
||||||
|
const A2dpConfigurationHint& hint,
|
||||||
|
A2dpConfiguration* configuration) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::bluetooth::audio
|
510
bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.cpp
Normal file
510
bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.cpp
Normal file
|
@ -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 <algorithm>
|
||||||
|
|
||||||
|
#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<CodecInfo::Transport::Tag::a2dp>();
|
||||||
|
auto& a2dp_info = info_.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||||
|
|
||||||
|
/* --- 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<uint8_t>& configuration,
|
||||||
|
CodecParameters* codec_parameters, SbcParameters* sbc_parameters) const {
|
||||||
|
auto& a2dp_info = info.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||||
|
|
||||||
|
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<uint8_t>& remote_capabilities,
|
||||||
|
const std::optional<CodecParameters>& hint,
|
||||||
|
std::vector<uint8_t>* configuration) const {
|
||||||
|
auto& a2dp_info = info.transport.get<CodecInfo::Transport::Tag::a2dp>();
|
||||||
|
|
||||||
|
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
|
61
bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.h
Normal file
61
bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.h
Normal file
|
@ -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<uint8_t>& configuration,
|
||||||
|
CodecParameters* codec_parameters,
|
||||||
|
SbcParameters* sbc_parameters) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const A2dpOffloadCodecSbc* GetInstance();
|
||||||
|
|
||||||
|
A2dpStatus ParseConfiguration(
|
||||||
|
const std::vector<uint8_t>& configuration,
|
||||||
|
CodecParameters* codec_parameters) const override {
|
||||||
|
return ParseConfiguration(configuration, codec_parameters, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
A2dpStatus ParseConfiguration(const std::vector<uint8_t>& configuration,
|
||||||
|
SbcParameters* sbc_parameters) const {
|
||||||
|
return ParseConfiguration(configuration, sbc_parameters, sbc_parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuildConfiguration(const std::vector<uint8_t>& remote_capabilities,
|
||||||
|
const std::optional<CodecParameters>& hint,
|
||||||
|
std::vector<uint8_t>* configuration) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::bluetooth::audio
|
|
@ -18,6 +18,9 @@ cc_library_shared {
|
||||||
"BluetoothAudioProvider.cpp",
|
"BluetoothAudioProvider.cpp",
|
||||||
"BluetoothAudioProviderFactory.cpp",
|
"BluetoothAudioProviderFactory.cpp",
|
||||||
"A2dpOffloadAudioProvider.cpp",
|
"A2dpOffloadAudioProvider.cpp",
|
||||||
|
"A2dpOffloadCodecAac.cpp",
|
||||||
|
"A2dpOffloadCodecFactory.cpp",
|
||||||
|
"A2dpOffloadCodecSbc.cpp",
|
||||||
"A2dpSoftwareAudioProvider.cpp",
|
"A2dpSoftwareAudioProvider.cpp",
|
||||||
"HearingAidAudioProvider.cpp",
|
"HearingAidAudioProvider.cpp",
|
||||||
"HfpOffloadAudioProvider.cpp",
|
"HfpOffloadAudioProvider.cpp",
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include <BluetoothAudioSessionReport.h>
|
#include <BluetoothAudioSessionReport.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
|
#include "A2dpOffloadCodecFactory.h"
|
||||||
|
|
||||||
namespace aidl {
|
namespace aidl {
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace hardware {
|
namespace hardware {
|
||||||
|
@ -164,6 +166,27 @@ ndk::ScopedAStatus BluetoothAudioProvider::setLowLatencyModeAllowed(
|
||||||
return ndk::ScopedAStatus::ok();
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus BluetoothAudioProvider::parseA2dpConfiguration(
|
||||||
|
[[maybe_unused]] const CodecId& codec_id,
|
||||||
|
[[maybe_unused]] const std::vector<uint8_t>& 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<A2dpRemoteCapabilities>&
|
||||||
|
remote_a2dp_capabilities,
|
||||||
|
[[maybe_unused]] const A2dpConfigurationHint& hint,
|
||||||
|
[[maybe_unused]] std::optional<audio::A2dpConfiguration>* _aidl_return) {
|
||||||
|
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
|
||||||
|
<< " is illegal";
|
||||||
|
|
||||||
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace audio
|
} // namespace audio
|
||||||
} // namespace bluetooth
|
} // namespace bluetooth
|
||||||
} // namespace hardware
|
} // namespace hardware
|
||||||
|
|
|
@ -50,6 +50,14 @@ class BluetoothAudioProvider : public BnBluetoothAudioProvider {
|
||||||
const AudioConfiguration& audio_config);
|
const AudioConfiguration& audio_config);
|
||||||
ndk::ScopedAStatus setLowLatencyModeAllowed(bool allowed);
|
ndk::ScopedAStatus setLowLatencyModeAllowed(bool allowed);
|
||||||
|
|
||||||
|
ndk::ScopedAStatus parseA2dpConfiguration(
|
||||||
|
const CodecId& codec_id, const std::vector<uint8_t>& configuration,
|
||||||
|
CodecParameters* codec_parameters, A2dpStatus* _aidl_return);
|
||||||
|
ndk::ScopedAStatus getA2dpConfiguration(
|
||||||
|
const std::vector<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
|
||||||
|
const A2dpConfigurationHint& hint,
|
||||||
|
std::optional<audio::A2dpConfiguration>* _aidl_return);
|
||||||
|
|
||||||
virtual bool isValid(const SessionType& sessionType) = 0;
|
virtual bool isValid(const SessionType& sessionType) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
#include "A2dpOffloadAudioProvider.h"
|
#include "A2dpOffloadAudioProvider.h"
|
||||||
|
#include "A2dpOffloadCodecFactory.h"
|
||||||
#include "A2dpSoftwareAudioProvider.h"
|
#include "A2dpSoftwareAudioProvider.h"
|
||||||
#include "BluetoothAudioProvider.h"
|
#include "BluetoothAudioProvider.h"
|
||||||
#include "HearingAidAudioProvider.h"
|
#include "HearingAidAudioProvider.h"
|
||||||
|
@ -150,7 +151,14 @@ ndk::ScopedAStatus BluetoothAudioProviderFactory::getProviderInfo(
|
||||||
SessionType session_type, std::optional<ProviderInfo>* _aidl_return) {
|
SessionType session_type, std::optional<ProviderInfo>* _aidl_return) {
|
||||||
*_aidl_return = std::nullopt;
|
*_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();
|
return ndk::ScopedAStatus::ok();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,11 @@
|
||||||
|
|
||||||
using aidl::android::hardware::audio::common::SinkMetadata;
|
using aidl::android::hardware::audio::common::SinkMetadata;
|
||||||
using aidl::android::hardware::audio::common::SourceMetadata;
|
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::AacCapabilities;
|
||||||
using aidl::android::hardware::bluetooth::audio::AacConfiguration;
|
using aidl::android::hardware::bluetooth::audio::AacConfiguration;
|
||||||
using aidl::android::hardware::bluetooth::audio::AptxAdaptiveLeCapabilities;
|
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::CodecConfiguration;
|
||||||
using aidl::android::hardware::bluetooth::audio::CodecId;
|
using aidl::android::hardware::bluetooth::audio::CodecId;
|
||||||
using aidl::android::hardware::bluetooth::audio::CodecInfo;
|
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::CodecType;
|
||||||
using aidl::android::hardware::bluetooth::audio::HfpConfiguration;
|
using aidl::android::hardware::bluetooth::audio::HfpConfiguration;
|
||||||
using aidl::android::hardware::bluetooth::audio::IBluetoothAudioPort;
|
using aidl::android::hardware::bluetooth::audio::IBluetoothAudioPort;
|
||||||
|
@ -707,6 +713,701 @@ TEST_P(BluetoothAudioProviderFactoryAidl, getProviderInfo_leAudioSessionTypes) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BluetoothAudioProviderAidl : public BluetoothAudioProviderFactoryAidl {
|
||||||
|
protected:
|
||||||
|
std::optional<IBluetoothAudioProviderFactory::ProviderInfo>
|
||||||
|
a2dp_encoding_provider_info_{};
|
||||||
|
std::optional<IBluetoothAudioProviderFactory::ProviderInfo>
|
||||||
|
a2dp_decoding_provider_info_{};
|
||||||
|
std::shared_ptr<IBluetoothAudioProvider> a2dp_encoding_provider_{nullptr};
|
||||||
|
std::shared_ptr<IBluetoothAudioProvider> a2dp_decoding_provider_{nullptr};
|
||||||
|
|
||||||
|
public:
|
||||||
|
void SetUp() override {
|
||||||
|
BluetoothAudioProviderFactoryAidl::SetUp();
|
||||||
|
audio_port_ = ndk::SharedRefBase::make<BluetoothAudioPort>();
|
||||||
|
|
||||||
|
(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<IBluetoothAudioProvider> 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<uint8_t>{}, &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<uint8_t>{}, &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<uint8_t>{}, &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<uint8_t>{},
|
||||||
|
&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<CodecInfo::Transport::a2dp>();
|
||||||
|
A2dpRemoteCapabilities remote_capabilities(/*seid*/ 0, codec_info.id,
|
||||||
|
transport.capabilities);
|
||||||
|
std::optional<A2dpConfiguration> configuration;
|
||||||
|
aidl_retval = provider->getA2dpConfiguration(
|
||||||
|
std::vector<A2dpRemoteCapabilities>{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<IBluetoothAudioProvider> provider{nullptr};
|
||||||
|
auto aidl_retval = provider_factory_->openProvider(session_type, &provider);
|
||||||
|
if (provider == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// getA2dpConfiguration must fail without returning a configuration.
|
||||||
|
std::optional<A2dpConfiguration> configuration;
|
||||||
|
aidl_retval =
|
||||||
|
provider->getA2dpConfiguration(std::vector<A2dpRemoteCapabilities>{},
|
||||||
|
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<A2dpConfiguration> configuration;
|
||||||
|
::ndk::ScopedAStatus aidl_retval;
|
||||||
|
|
||||||
|
// Test with empty remote capabilities.
|
||||||
|
aidl_retval =
|
||||||
|
provider->getA2dpConfiguration(std::vector<A2dpRemoteCapabilities>{},
|
||||||
|
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<uint8_t>{1, 2, 3});
|
||||||
|
A2dpRemoteCapabilities unknown_vendor_remote_capabilities(
|
||||||
|
/*seid*/ 1,
|
||||||
|
/* Google Codec #42 */ CodecId::Vendor(0xFCB1, 0x42),
|
||||||
|
std::vector<uint8_t>{1, 2, 3});
|
||||||
|
aidl_retval = provider->getA2dpConfiguration(
|
||||||
|
std::vector<A2dpRemoteCapabilities>{
|
||||||
|
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<A2dpConfiguration> 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<CodecInfo::Transport::a2dp>();
|
||||||
|
std::vector<uint8_t> invalid_capabilities = transport.capabilities;
|
||||||
|
invalid_capabilities.push_back(0x42); // adding bytes should be invalid.
|
||||||
|
aidl_retval = provider->getA2dpConfiguration(
|
||||||
|
std::vector<A2dpRemoteCapabilities>{
|
||||||
|
A2dpRemoteCapabilities(/*seid*/ 0, codec_info.id,
|
||||||
|
std::vector<uint8_t>()),
|
||||||
|
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<CodecInfo::Transport::a2dp>();
|
||||||
|
std::optional<A2dpConfiguration> configuration{};
|
||||||
|
::ndk::ScopedAStatus aidl_retval;
|
||||||
|
|
||||||
|
aidl_retval = provider->getA2dpConfiguration(
|
||||||
|
std::vector<A2dpRemoteCapabilities>{
|
||||||
|
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<A2dpRemoteCapabilities> 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<CodecInfo::Transport::a2dp>();
|
||||||
|
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<A2dpConfiguration> 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<A2dpConfiguration> 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<CodecInfo::Transport::a2dp>();
|
||||||
|
std::optional<A2dpConfiguration> 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>{
|
||||||
|
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<CodecInfo::Transport::a2dp>();
|
||||||
|
std::optional<A2dpConfiguration> 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>{
|
||||||
|
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<CodecInfo::Transport::a2dp>();
|
||||||
|
std::optional<A2dpConfiguration> 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>{
|
||||||
|
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<uint8_t>{1, 2, 3};
|
||||||
|
|
||||||
|
auto aidl_retval =
|
||||||
|
provider->startSession(audio_port_, AudioConfiguration(a2dp_config),
|
||||||
|
std::vector<LatencyMode>{}, &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<CodecInfo::Transport::a2dp>();
|
||||||
|
::ndk::ScopedAStatus aidl_retval;
|
||||||
|
A2dpRemoteCapabilities remote_capabilities(/*seid*/ 0, codec_info.id,
|
||||||
|
a2dp_info.capabilities);
|
||||||
|
std::optional<A2dpConfiguration> configuration;
|
||||||
|
aidl_retval = provider->getA2dpConfiguration(
|
||||||
|
std::vector<A2dpRemoteCapabilities>{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<LatencyMode>{}, &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<CodecInfo::Transport::a2dp>();
|
||||||
|
A2dpRemoteCapabilities remote_capabilities(/*seid*/ 0, codec_info.id,
|
||||||
|
a2dp_info.capabilities);
|
||||||
|
std::optional<A2dpConfiguration> configuration;
|
||||||
|
aidl_retval = provider->getA2dpConfiguration(
|
||||||
|
std::vector<A2dpRemoteCapabilities>{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<LatencyMode>{}, &data_mq_desc);
|
||||||
|
|
||||||
|
EXPECT_FALSE(aidl_retval.isOk());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* openProvider A2DP_SOFTWARE_ENCODING_DATAPATH
|
* openProvider A2DP_SOFTWARE_ENCODING_DATAPATH
|
||||||
*/
|
*/
|
||||||
|
@ -2393,6 +3094,12 @@ INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothAudioProviderFactoryAidl,
|
||||||
IBluetoothAudioProviderFactory::descriptor)),
|
IBluetoothAudioProviderFactory::descriptor)),
|
||||||
android::PrintInstanceNameToString);
|
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(
|
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
|
||||||
BluetoothAudioProviderA2dpEncodingSoftwareAidl);
|
BluetoothAudioProviderA2dpEncodingSoftwareAidl);
|
||||||
INSTANTIATE_TEST_SUITE_P(PerInstance,
|
INSTANTIATE_TEST_SUITE_P(PerInstance,
|
||||||
|
|
|
@ -304,7 +304,8 @@ bool BluetoothAudioSession::UpdateAudioConfig(
|
||||||
audio_config_tag == AudioConfiguration::pcmConfig);
|
audio_config_tag == AudioConfiguration::pcmConfig);
|
||||||
bool is_a2dp_offload_audio_config =
|
bool is_a2dp_offload_audio_config =
|
||||||
(is_offload_a2dp_session &&
|
(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 =
|
bool is_hfp_offload_audio_config =
|
||||||
(is_offload_hfp_session &&
|
(is_offload_hfp_session &&
|
||||||
audio_config_tag == AudioConfiguration::hfpConfig);
|
audio_config_tag == AudioConfiguration::hfpConfig);
|
||||||
|
|
|
@ -467,6 +467,8 @@ inline AudioConfig_2_1 to_hidl_audio_config_2_1(
|
||||||
hidl_audio_config.codecConfig(to_hidl_codec_config_2_0(
|
hidl_audio_config.codecConfig(to_hidl_codec_config_2_0(
|
||||||
audio_config.get<AudioConfiguration::a2dpConfig>()));
|
audio_config.get<AudioConfiguration::a2dpConfig>()));
|
||||||
break;
|
break;
|
||||||
|
case AudioConfiguration::a2dp:
|
||||||
|
break;
|
||||||
case AudioConfiguration::leAudioConfig:
|
case AudioConfiguration::leAudioConfig:
|
||||||
hidl_audio_config.leAudioCodecConfig(to_hidl_leaudio_config_2_1(
|
hidl_audio_config.leAudioCodecConfig(to_hidl_leaudio_config_2_1(
|
||||||
audio_config.get<AudioConfiguration::leAudioConfig>()));
|
audio_config.get<AudioConfiguration::leAudioConfig>()));
|
||||||
|
|
Loading…
Reference in a new issue