audio: Implement the major functionality of the primary CF HAL

Core HAL changes:

1. Add StreamPrimary implemented via StreamAlsa.

2. Align the configuration with the HIDL HAL.

3. Fix position retrieval vs. standby call.

4. Fix sleeps in StreamAlsa.

VTS changes:

1. Use several bursts for stream I/O test scenarios that check
   observable position increase. This is because the position may
   not be available until a couple of transfers have been made.

2. Do not require position increase for the scenarios that do
   not make several bursts. As specified above, the position may
   not have been increased for the ALSA case. Whereas, using
   multiple bursts in all scenarios will increase test time, and
   make the state machine transitions graph more complicated.

3. Hook up the test config file to shut down audioserver during
   VTS tests, fix the test config file.

Bug: 286914845
Test: atest VtsHalAudioCoreTargetTest
Test: compare APM dumps for AIDL vs. HIDL
Change-Id: I85271564c664fa40008d60e82b32eaa66a99c68f
This commit is contained in:
Mikhail Naganov 2023-07-24 14:51:36 -07:00
parent e8d695d9b2
commit cf824f65c8
20 changed files with 637 additions and 172 deletions

View file

@ -85,6 +85,8 @@ cc_library {
"bluetooth/DevicePortProxy.cpp",
"bluetooth/ModuleBluetooth.cpp",
"bluetooth/StreamBluetooth.cpp",
"primary/PrimaryMixer.cpp",
"primary/StreamPrimary.cpp",
"r_submix/ModuleRemoteSubmix.cpp",
"r_submix/RemoteSubmixUtils.cpp",
"r_submix/SubmixRoute.cpp",

View file

@ -136,7 +136,7 @@ static AudioRoute createRoute(const std::vector<AudioPort>& sources, const Audio
// Device ports:
// * "Speaker", OUT_SPEAKER, default
// - no profiles specified
// * "Built-in Mic", IN_MICROPHONE, default
// * "Built-In Mic", IN_MICROPHONE, default
// - no profiles specified
// * "Telephony Tx", OUT_TELEPHONY_TX
// - no profiles specified
@ -148,45 +148,34 @@ static AudioRoute createRoute(const std::vector<AudioPort>& sources, const Audio
// Mix ports:
// * "primary output", PRIMARY, 1 max open, 1 max active stream
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
// * "compressed offload", DIRECT|COMPRESS_OFFLOAD|NON_BLOCKING, 1 max open, 1 max active stream
// - profile MP3; MONO, STEREO; 44100, 48000
// * "primary input", 2 max open, 2 max active streams
// - profile PCM 16-bit; MONO, STEREO, FRONT_BACK;
// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
// - profile PCM 24-bit; MONO, STEREO, FRONT_BACK;
// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
// * "primary input", 1 max open, 1 max active stream
// - profile PCM 16-bit; MONO, STEREO;
// 8000, 11025, 16000, 32000, 44100, 48000
// * "telephony_tx", 1 max open, 1 max active stream
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
// * "telephony_rx", 1 max open, 1 max active stream
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
// * "fm_tuner", 1 max open, 1 max active stream
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
//
// Routes:
// "primary out", "compressed offload" -> "Speaker"
// "Built-in Mic" -> "primary input"
// "telephony_tx" -> "Telephony Tx"
// "primary out" -> "Speaker"
// "Built-In Mic" -> "primary input"
// "Telephony Rx" -> "telephony_rx"
// "telephony_tx" -> "Telephony Tx"
// "FM Tuner" -> "fm_tuner"
//
// Initial port configs:
// * "Speaker" device port: PCM 24-bit; STEREO; 48000
// * "Built-in Mic" device port: PCM 24-bit; MONO; 48000
// * "Telephony Tx" device port: PCM 24-bit; MONO; 48000
// * "Telephony Rx" device port: PCM 24-bit; MONO; 48000
// * "FM Tuner" device port: PCM 24-bit; STEREO; 48000
// * "Speaker" device port: PCM 16-bit; STEREO; 48000
// * "Built-In Mic" device port: PCM 16-bit; MONO; 48000
// * "Telephony Tx" device port: PCM 16-bit; MONO; 48000
// * "Telephony Rx" device port: PCM 16-bit; MONO; 48000
// * "FM Tuner" device port: PCM 16-bit; STEREO; 48000
//
std::unique_ptr<Configuration> getPrimaryConfiguration() {
static const Configuration configuration = []() {
const std::vector<AudioProfile> standardPcmAudioProfiles = {
createProfile(PcmType::INT_16_BIT,
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
{8000, 11025, 16000, 32000, 44100, 48000}),
createProfile(PcmType::INT_24_BIT,
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
{8000, 11025, 16000, 32000, 44100, 48000})};
Configuration c;
@ -199,17 +188,17 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE));
c.ports.push_back(speakerOutDevice);
c.initialConfigs.push_back(
createPortConfig(speakerOutDevice.id, speakerOutDevice.id, PcmType::INT_24_BIT,
createPortConfig(speakerOutDevice.id, speakerOutDevice.id, PcmType::INT_16_BIT,
AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false,
createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0)));
AudioPort micInDevice =
createPort(c.nextPortId++, "Built-in Mic", 0, true,
createPort(c.nextPortId++, "Built-In Mic", 0, true,
createDeviceExt(AudioDeviceType::IN_MICROPHONE,
1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE));
c.ports.push_back(micInDevice);
c.initialConfigs.push_back(
createPortConfig(micInDevice.id, micInDevice.id, PcmType::INT_24_BIT,
createPortConfig(micInDevice.id, micInDevice.id, PcmType::INT_16_BIT,
AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0)));
@ -219,7 +208,7 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
c.ports.push_back(telephonyTxOutDevice);
c.initialConfigs.push_back(
createPortConfig(telephonyTxOutDevice.id, telephonyTxOutDevice.id,
PcmType::INT_24_BIT, AudioChannelLayout::LAYOUT_MONO, 48000, 0,
PcmType::INT_16_BIT, AudioChannelLayout::LAYOUT_MONO, 48000, 0,
false, createDeviceExt(AudioDeviceType::OUT_TELEPHONY_TX, 0)));
AudioPort telephonyRxInDevice =
@ -228,14 +217,14 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
c.ports.push_back(telephonyRxInDevice);
c.initialConfigs.push_back(
createPortConfig(telephonyRxInDevice.id, telephonyRxInDevice.id,
PcmType::INT_24_BIT, AudioChannelLayout::LAYOUT_MONO, 48000, 0,
PcmType::INT_16_BIT, AudioChannelLayout::LAYOUT_MONO, 48000, 0,
true, createDeviceExt(AudioDeviceType::IN_TELEPHONY_RX, 0)));
AudioPort fmTunerInDevice = createPort(c.nextPortId++, "FM Tuner", 0, true,
createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0));
c.ports.push_back(fmTunerInDevice);
c.initialConfigs.push_back(
createPortConfig(fmTunerInDevice.id, fmTunerInDevice.id, PcmType::INT_24_BIT,
createPortConfig(fmTunerInDevice.id, fmTunerInDevice.id, PcmType::INT_16_BIT,
AudioChannelLayout::LAYOUT_STEREO, 48000, 0, true,
createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0)));
@ -249,30 +238,12 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
standardPcmAudioProfiles.end());
c.ports.push_back(primaryOutMix);
AudioPort compressedOffloadOutMix =
createPort(c.nextPortId++, "compressed offload",
makeBitPositionFlagMask({AudioOutputFlags::DIRECT,
AudioOutputFlags::COMPRESS_OFFLOAD,
AudioOutputFlags::NON_BLOCKING}),
false, createPortMixExt(1, 1));
compressedOffloadOutMix.profiles.push_back(
createProfile(::android::MEDIA_MIMETYPE_AUDIO_MPEG,
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
{44100, 48000}));
c.ports.push_back(compressedOffloadOutMix);
AudioPort primaryInMix =
createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(2, 2));
createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(1, 1));
primaryInMix.profiles.push_back(
createProfile(PcmType::INT_16_BIT,
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
AudioChannelLayout::LAYOUT_FRONT_BACK},
{8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
primaryInMix.profiles.push_back(
createProfile(PcmType::INT_24_BIT,
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
AudioChannelLayout::LAYOUT_FRONT_BACK},
{8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
{8000, 11025, 16000, 32000, 44100, 48000}));
c.ports.push_back(primaryInMix);
AudioPort telephonyTxOutMix =
@ -296,10 +267,10 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
standardPcmAudioProfiles.end());
c.ports.push_back(fmTunerInMix);
c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, speakerOutDevice));
c.routes.push_back(createRoute({primaryOutMix}, speakerOutDevice));
c.routes.push_back(createRoute({micInDevice}, primaryInMix));
c.routes.push_back(createRoute({telephonyTxOutMix}, telephonyTxOutDevice));
c.routes.push_back(createRoute({telephonyRxInDevice}, telephonyRxInMix));
c.routes.push_back(createRoute({telephonyTxOutMix}, telephonyTxOutDevice));
c.routes.push_back(createRoute({fmTunerInDevice}, fmTunerInMix));
c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
@ -320,15 +291,15 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
//
// Device ports:
// * "Remote Submix Out", OUT_SUBMIX
// - profile PCM 24-bit; STEREO; 48000
// - profile PCM 16-bit; STEREO; 48000
// * "Remote Submix In", IN_SUBMIX
// - profile PCM 24-bit; STEREO; 48000
// - profile PCM 16-bit; STEREO; 48000
//
// Mix ports:
// * "r_submix output", stream count unlimited
// - profile PCM 24-bit; STEREO; 48000
// * "r_submix input", stream count unlimited
// - profile PCM 24-bit; STEREO; 48000
// * "r_submix output", 1 max open, 1 max active stream
// - profile PCM 16-bit; STEREO; 48000
// * "r_submix input", 1 max open, 1 max active stream
// - profile PCM 16-bit; STEREO; 48000
//
// Routes:
// "r_submix output" -> "Remote Submix Out"
@ -345,27 +316,27 @@ std::unique_ptr<Configuration> getRSubmixConfiguration() {
createDeviceExt(AudioDeviceType::OUT_SUBMIX, 0,
AudioDeviceDescription::CONNECTION_VIRTUAL));
rsubmixOutDevice.profiles.push_back(
createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
c.ports.push_back(rsubmixOutDevice);
AudioPort rsubmixInDevice = createPort(c.nextPortId++, "Remote Submix In", 0, true,
createDeviceExt(AudioDeviceType::IN_SUBMIX, 0));
rsubmixInDevice.profiles.push_back(
createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
c.ports.push_back(rsubmixInDevice);
// Mix ports
AudioPort rsubmixOutMix =
createPort(c.nextPortId++, "r_submix output", 0, false, createPortMixExt(0, 0));
createPort(c.nextPortId++, "r_submix output", 0, false, createPortMixExt(1, 1));
rsubmixOutMix.profiles.push_back(
createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
c.ports.push_back(rsubmixOutMix);
AudioPort rsubmixInMix =
createPort(c.nextPortId++, "r_submix input", 0, true, createPortMixExt(0, 0));
createPort(c.nextPortId++, "r_submix input", 0, true, createPortMixExt(1, 1));
rsubmixInMix.profiles.push_back(
createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
c.ports.push_back(rsubmixInMix);
c.routes.push_back(createRoute({rsubmixOutMix}, rsubmixOutDevice));
@ -483,7 +454,7 @@ std::unique_ptr<Configuration> getUsbConfiguration() {
// - profile MP3; MONO, STEREO; 44100, 48000
// * "test input", 2 max open, 2 max active streams
// - profile PCM 24-bit; MONO, STEREO, FRONT_BACK;
// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
// 8000, 11025, 16000, 22050, 32000, 44100, 48000
//
// Routes:
// "test output", "compressed offload" -> "Test Out"
@ -543,12 +514,12 @@ std::unique_ptr<Configuration> getStubConfiguration() {
createProfile(PcmType::INT_16_BIT,
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
AudioChannelLayout::LAYOUT_FRONT_BACK},
{8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
{8000, 11025, 16000, 22050, 32000, 44100, 48000}));
testInMIx.profiles.push_back(
createProfile(PcmType::INT_24_BIT,
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
AudioChannelLayout::LAYOUT_FRONT_BACK},
{8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
{8000, 11025, 16000, 22050, 32000, 44100, 48000}));
c.ports.push_back(testInMIx);
c.routes.push_back(createRoute({testOutMix, compressedOffloadOutMix}, testOutDevice));
@ -566,7 +537,7 @@ std::unique_ptr<Configuration> getStubConfiguration() {
// Device ports:
// * "BT A2DP Out", OUT_DEVICE, CONNECTION_BT_A2DP
// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
// * "BT A2DP Headphones", OUT_HEADSET, CONNECTION_BT_A2DP
// * "BT A2DP Headphones", OUT_HEADPHONE, CONNECTION_BT_A2DP
// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
// * "BT A2DP Speaker", OUT_SPEAKER, CONNECTION_BT_A2DP
// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
@ -597,13 +568,18 @@ std::unique_ptr<Configuration> getBluetoothConfiguration() {
createPort(c.nextPortId++, "BT A2DP Out", 0, false,
createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
AudioDeviceDescription::CONNECTION_BT_A2DP));
btOutDevice.profiles.insert(btOutDevice.profiles.begin(), standardPcmAudioProfiles.begin(),
standardPcmAudioProfiles.end());
c.ports.push_back(btOutDevice);
c.connectedProfiles[btOutDevice.id] = standardPcmAudioProfiles;
AudioPort btOutHeadphone =
createPort(c.nextPortId++, "BT A2DP Headphones", 0, false,
createDeviceExt(AudioDeviceType::OUT_HEADSET, 0,
createDeviceExt(AudioDeviceType::OUT_HEADPHONE, 0,
AudioDeviceDescription::CONNECTION_BT_A2DP));
btOutHeadphone.profiles.insert(btOutHeadphone.profiles.begin(),
standardPcmAudioProfiles.begin(),
standardPcmAudioProfiles.end());
c.ports.push_back(btOutHeadphone);
c.connectedProfiles[btOutHeadphone.id] = standardPcmAudioProfiles;
@ -611,6 +587,9 @@ std::unique_ptr<Configuration> getBluetoothConfiguration() {
createPort(c.nextPortId++, "BT A2DP Speaker", 0, false,
createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0,
AudioDeviceDescription::CONNECTION_BT_A2DP));
btOutSpeaker.profiles.insert(btOutSpeaker.profiles.begin(),
standardPcmAudioProfiles.begin(),
standardPcmAudioProfiles.end());
c.ports.push_back(btOutSpeaker);
c.connectedProfiles[btOutSpeaker.id] = standardPcmAudioProfiles;
@ -623,20 +602,20 @@ std::unique_ptr<Configuration> getBluetoothConfiguration() {
{createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000})});
// Mix ports
AudioPort btInMix =
createPort(c.nextPortId++, "a2dp output", 0, true, createPortMixExt(1, 1));
c.ports.push_back(btInMix);
AudioPort btOutMix =
createPort(c.nextPortId++, "a2dp output", 0, false, createPortMixExt(1, 1));
c.ports.push_back(btOutMix);
AudioPort btHeadsetInMix =
createPort(c.nextPortId++, "hearing aid output", 0, true, createPortMixExt(1, 1));
btHeadsetInMix.profiles.push_back(createProfile(
AudioPort btHearingOutMix =
createPort(c.nextPortId++, "hearing aid output", 0, false, createPortMixExt(1, 1));
btHearingOutMix.profiles.push_back(createProfile(
PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000, 24000}));
c.ports.push_back(btHeadsetInMix);
c.ports.push_back(btHearingOutMix);
c.routes.push_back(createRoute({btInMix}, btOutDevice));
c.routes.push_back(createRoute({btInMix}, btOutHeadphone));
c.routes.push_back(createRoute({btInMix}, btOutSpeaker));
c.routes.push_back(createRoute({btHeadsetInMix}, btOutHearingAid));
c.routes.push_back(createRoute({btOutMix}, btOutDevice));
c.routes.push_back(createRoute({btOutMix}, btOutHeadphone));
c.routes.push_back(createRoute({btOutMix}, btOutSpeaker));
c.routes.push_back(createRoute({btHearingOutMix}, btOutHearingAid));
return c;
}();

View file

@ -21,7 +21,7 @@
#include <android-base/logging.h>
#include "core-impl/ModulePrimary.h"
#include "core-impl/StreamStub.h"
#include "core-impl/StreamPrimary.h"
#include "core-impl/Telephony.h"
using aidl::android::hardware::audio::common::SinkMetadata;
@ -47,15 +47,15 @@ ndk::ScopedAStatus ModulePrimary::createInputStream(StreamContext&& context,
const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) {
return createStreamInstance<StreamInStub>(result, std::move(context), sinkMetadata,
microphones);
return createStreamInstance<StreamInPrimary>(result, std::move(context), sinkMetadata,
microphones);
}
ndk::ScopedAStatus ModulePrimary::createOutputStream(
StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
return createStreamInstance<StreamOutStub>(result, std::move(context), sourceMetadata,
offloadInfo);
return createStreamInstance<StreamOutPrimary>(result, std::move(context), sourceMetadata,
offloadInfo);
}
} // namespace aidl::android::hardware::audio::core

View file

@ -238,8 +238,8 @@ StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
break;
case Tag::standby:
if (mState == StreamDescriptor::State::IDLE) {
populateReply(&reply, mIsConnected);
if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
populateReply(&reply, mIsConnected);
mState = StreamDescriptor::State::STANDBY;
} else {
LOG(ERROR) << __func__ << ": standby failed: " << status;
@ -492,8 +492,8 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
break;
case Tag::standby:
if (mState == StreamDescriptor::State::IDLE) {
populateReply(&reply, mIsConnected);
if (::android::status_t status = mDriver->standby(); status == ::android::OK) {
populateReply(&reply, mIsConnected);
mState = StreamDescriptor::State::STANDBY;
} else {
LOG(ERROR) << __func__ << ": standby failed: " << status;
@ -799,6 +799,32 @@ ndk::ScopedAStatus StreamIn::setHwGain(const std::vector<float>& in_channelGains
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
StreamInHwGainHelper::StreamInHwGainHelper(const StreamContext* context)
: mChannelCount(getChannelCount(context->getChannelLayout())) {}
ndk::ScopedAStatus StreamInHwGainHelper::getHwGainImpl(std::vector<float>* _aidl_return) {
*_aidl_return = mHwGains;
LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamInHwGainHelper::setHwGainImpl(const std::vector<float>& in_channelGains) {
LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
if (in_channelGains.size() != mChannelCount) {
LOG(ERROR) << __func__
<< ": channel count does not match stream channel count: " << mChannelCount;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
for (float gain : in_channelGains) {
if (gain < StreamIn::HW_GAIN_MIN || gain > StreamIn::HW_GAIN_MAX) {
LOG(ERROR) << __func__ << ": gain value out of range: " << gain;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
mHwGains = in_channelGains;
return ndk::ScopedAStatus::ok();
}
StreamOut::StreamOut(StreamContext&& context, const std::optional<AudioOffloadInfo>& offloadInfo)
: mContextInstance(std::move(context)), mOffloadInfo(offloadInfo) {
LOG(DEBUG) << __func__;
@ -904,4 +930,31 @@ ndk::ScopedAStatus StreamOut::selectPresentation(int32_t in_presentationId, int3
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
StreamOutHwVolumeHelper::StreamOutHwVolumeHelper(const StreamContext* context)
: mChannelCount(getChannelCount(context->getChannelLayout())) {}
ndk::ScopedAStatus StreamOutHwVolumeHelper::getHwVolumeImpl(std::vector<float>* _aidl_return) {
*_aidl_return = mHwVolumes;
LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamOutHwVolumeHelper::setHwVolumeImpl(
const std::vector<float>& in_channelVolumes) {
LOG(DEBUG) << __func__ << ": volumes " << ::android::internal::ToString(in_channelVolumes);
if (in_channelVolumes.size() != mChannelCount) {
LOG(ERROR) << __func__
<< ": channel count does not match stream channel count: " << mChannelCount;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
for (float volume : in_channelVolumes) {
if (volume < StreamOut::HW_VOLUME_MIN || volume > StreamOut::HW_VOLUME_MAX) {
LOG(ERROR) << __func__ << ": volume value out of range: " << volume;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
mHwVolumes = in_channelVolumes;
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::audio::core

View file

@ -14,6 +14,7 @@
* limitations under the License.
*/
#include <cmath>
#include <limits>
#define LOG_TAG "AHAL_StreamAlsa"
@ -29,7 +30,9 @@ namespace aidl::android::hardware::audio::core {
StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int readWriteRetries)
: StreamCommonImpl(context, metadata),
mBufferSizeFrames(getContext().getBufferSizeInFrames()),
mFrameSizeBytes(getContext().getFrameSize()),
mSampleRate(getContext().getSampleRate()),
mIsInput(isInput(metadata)),
mConfig(alsa::getPcmConfig(getContext(), mIsInput)),
mReadWriteRetries(readWriteRetries) {}
@ -39,17 +42,20 @@ StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int rea
}
::android::status_t StreamAlsa::drain(StreamDescriptor::DrainMode) {
usleep(1000);
if (!mIsInput) {
static constexpr float kMicrosPerSecond = MICROS_PER_SECOND;
const size_t delayUs = static_cast<size_t>(
std::roundf(mBufferSizeFrames * kMicrosPerSecond / mSampleRate));
usleep(delayUs);
}
return ::android::OK;
}
::android::status_t StreamAlsa::flush() {
usleep(1000);
return ::android::OK;
}
::android::status_t StreamAlsa::pause() {
usleep(1000);
return ::android::OK;
}
@ -59,6 +65,10 @@ StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int rea
}
::android::status_t StreamAlsa::start() {
if (!mAlsaDeviceProxies.empty()) {
// This is a resume after a pause.
return ::android::OK;
}
decltype(mAlsaDeviceProxies) alsaDeviceProxies;
for (const auto& device : getDeviceProfiles()) {
alsa::DeviceProxy proxy;
@ -71,8 +81,7 @@ StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int rea
true /*require_exact_match*/);
} else {
proxy = alsa::openProxyForAttachedDevice(
device, const_cast<struct pcm_config*>(&mConfig.value()),
getContext().getBufferSizeInFrames());
device, const_cast<struct pcm_config*>(&mConfig.value()), mBufferSizeFrames);
}
if (!proxy) {
return ::android::NO_INIT;
@ -85,13 +94,13 @@ StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int rea
::android::status_t StreamAlsa::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) {
if (mAlsaDeviceProxies.empty()) {
LOG(FATAL) << __func__ << ": no opened devices";
return ::android::NO_INIT;
}
const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
unsigned maxLatency = 0;
if (mIsInput) {
if (mAlsaDeviceProxies.empty()) {
LOG(FATAL) << __func__ << ": no input devices";
return ::android::NO_INIT;
}
// For input case, only support single device.
proxy_read_with_retries(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer,
mReadWriteRetries);
@ -110,9 +119,12 @@ StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int rea
::android::status_t StreamAlsa::refinePosition(StreamDescriptor::Position* position) {
if (mAlsaDeviceProxies.empty()) {
LOG(FATAL) << __func__ << ": no input devices";
LOG(FATAL) << __func__ << ": no opened devices";
return ::android::NO_INIT;
}
// Since the proxy can only count transferred frames since its creation,
// we override its counter value with ours and let it to correct for buffered frames.
alsa::resetTransferredFrames(mAlsaDeviceProxies[0], position->frames);
if (mIsInput) {
if (int ret = proxy_get_capture_position(mAlsaDeviceProxies[0].get(), &position->frames,
&position->timeNs);

View file

@ -262,12 +262,14 @@ std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile) {
}
DeviceProxy makeDeviceProxy() {
return DeviceProxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
DeviceProxy proxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
if (proxy != nullptr) {
proxy_close(proxy);
delete proxy;
}
});
memset(proxy.get(), 0, sizeof(alsa_device_proxy));
return proxy;
}
DeviceProxy openProxyForAttachedDevice(const DeviceProfile& deviceProfile,
@ -334,6 +336,12 @@ std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& devic
return profile;
}
void resetTransferredFrames(DeviceProxy& proxy, uint64_t frames) {
if (proxy != nullptr) {
proxy->transferred = frames;
}
}
AudioFormatDescription c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) {
return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription());
}

View file

@ -66,6 +66,7 @@ DeviceProxy openProxyForAttachedDevice(const DeviceProfile& deviceProfile,
DeviceProxy openProxyForExternalDevice(const DeviceProfile& deviceProfile,
struct pcm_config* pcmConfig, bool requireExactMatch);
std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile);
void resetTransferredFrames(DeviceProxy& proxy, uint64_t frames);
::aidl::android::media::audio::common::AudioFormatDescription
c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy);

View file

@ -511,6 +511,17 @@ class StreamIn : virtual public StreamCommonInterface, public BnStreamIn {
const std::map<::aidl::android::media::audio::common::AudioDevice, std::string> mMicrophones;
};
class StreamInHwGainHelper {
protected:
explicit StreamInHwGainHelper(const StreamContext* context);
ndk::ScopedAStatus getHwGainImpl(std::vector<float>* _aidl_return);
ndk::ScopedAStatus setHwGainImpl(const std::vector<float>& in_channelGains);
const size_t mChannelCount;
std::vector<float> mHwGains;
};
class StreamOut : virtual public StreamCommonInterface, public BnStreamOut {
protected:
void defaultOnClose();
@ -557,6 +568,17 @@ class StreamOut : virtual public StreamCommonInterface, public BnStreamOut {
std::optional<::aidl::android::hardware::audio::common::AudioOffloadMetadata> mOffloadMetadata;
};
class StreamOutHwVolumeHelper {
protected:
explicit StreamOutHwVolumeHelper(const StreamContext* context);
ndk::ScopedAStatus getHwVolumeImpl(std::vector<float>* _aidl_return);
ndk::ScopedAStatus setHwVolumeImpl(const std::vector<float>& in_channelVolumes);
const size_t mChannelCount;
std::vector<float> mHwVolumes;
};
// The recommended way to create a stream instance.
// 'StreamImpl' is the concrete stream implementation, 'StreamInOrOut' is either 'StreamIn' or
// 'StreamOut', the rest are the arguments forwarded to the constructor of 'StreamImpl'.

View file

@ -48,7 +48,9 @@ class StreamAlsa : public StreamCommonImpl {
// Called from 'start' to initialize 'mAlsaDeviceProxies', the vector must be non-empty.
virtual std::vector<alsa::DeviceProfile> getDeviceProfiles() = 0;
const size_t mBufferSizeFrames;
const size_t mFrameSizeBytes;
const int mSampleRate;
const bool mIsInput;
const std::optional<struct pcm_config> mConfig;
const int mReadWriteRetries;

View file

@ -0,0 +1,84 @@
/*
* 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 <vector>
#include "StreamAlsa.h"
#include "StreamSwitcher.h"
namespace aidl::android::hardware::audio::core {
class StreamPrimary : public StreamAlsa {
public:
StreamPrimary(StreamContext* context, const Metadata& metadata);
protected:
std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
const bool mIsInput;
};
class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper {
public:
friend class ndk::SharedRefBase;
StreamInPrimary(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
static bool useStubStream(const ::aidl::android::media::audio::common::AudioDevice& device);
DeviceSwitchBehavior switchCurrentStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
override;
std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
StreamContext* context, const Metadata& metadata) override;
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getHwGain(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwGain(const std::vector<float>& in_channelGains) override;
};
class StreamOutPrimary final : public StreamOut,
public StreamSwitcher,
public StreamOutHwVolumeHelper {
public:
friend class ndk::SharedRefBase;
StreamOutPrimary(StreamContext&& context,
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
private:
static bool useStubStream(const ::aidl::android::media::audio::common::AudioDevice& device);
DeviceSwitchBehavior switchCurrentStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
override;
std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
StreamContext* context, const Metadata& metadata) override;
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
};
} // namespace aidl::android::hardware::audio::core

View file

@ -35,6 +35,7 @@ class StreamStub : public StreamCommonImpl {
void shutdown() override;
private:
const size_t mBufferSizeFrames;
const size_t mFrameSizeBytes;
const int mSampleRate;
const bool mIsAsynchronous;

View file

@ -59,7 +59,7 @@ class StreamInUsb final : public StreamIn, public StreamUsb {
override;
};
class StreamOutUsb final : public StreamOut, public StreamUsb {
class StreamOutUsb final : public StreamOut, public StreamUsb, public StreamOutHwVolumeHelper {
public:
friend class ndk::SharedRefBase;
StreamOutUsb(StreamContext&& context,
@ -71,9 +71,6 @@ class StreamOutUsb final : public StreamOut, public StreamUsb {
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
const int mChannelCount;
std::vector<float> mHwVolumes;
};
} // namespace aidl::android::hardware::audio::core

View file

@ -0,0 +1,29 @@
/*
* 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.
*/
#define LOG_TAG "AHAL_PrimaryMixer"
#include "PrimaryMixer.h"
namespace aidl::android::hardware::audio::core::primary {
// static
PrimaryMixer& PrimaryMixer::getInstance() {
static PrimaryMixer gInstance;
return gInstance;
}
} // namespace aidl::android::hardware::audio::core::primary

View file

@ -0,0 +1,42 @@
/*
* 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 <map>
#include <memory>
#include <mutex>
#include <vector>
#include <android-base/thread_annotations.h>
#include <android/binder_auto_utils.h>
#include "alsa/Mixer.h"
namespace aidl::android::hardware::audio::core::primary {
class PrimaryMixer : public alsa::Mixer {
public:
static constexpr int kAlsaCard = 0;
static constexpr int kAlsaDevice = 0;
static PrimaryMixer& getInstance();
private:
PrimaryMixer() : alsa::Mixer(kAlsaCard) {}
};
} // namespace aidl::android::hardware::audio::core::primary

View file

@ -0,0 +1,186 @@
/*
* 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 <limits>
#define LOG_TAG "AHAL_StreamPrimary"
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <error/expected_utils.h>
#include "PrimaryMixer.h"
#include "core-impl/StreamPrimary.h"
#include "core-impl/StreamStub.h"
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::MicrophoneInfo;
using android::base::GetBoolProperty;
namespace aidl::android::hardware::audio::core {
StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata)
: StreamAlsa(context, metadata, 3 /*readWriteRetries*/), mIsInput(isInput(metadata)) {}
std::vector<alsa::DeviceProfile> StreamPrimary::getDeviceProfiles() {
static const std::vector<alsa::DeviceProfile> kBuiltInSource{
alsa::DeviceProfile{.card = primary::PrimaryMixer::kAlsaCard,
.device = primary::PrimaryMixer::kAlsaDevice,
.direction = PCM_IN,
.isExternal = false}};
static const std::vector<alsa::DeviceProfile> kBuiltInSink{
alsa::DeviceProfile{.card = primary::PrimaryMixer::kAlsaCard,
.device = primary::PrimaryMixer::kAlsaDevice,
.direction = PCM_OUT,
.isExternal = false}};
return mIsInput ? kBuiltInSource : kBuiltInSink;
}
StreamInPrimary::StreamInPrimary(StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones)
: StreamIn(std::move(context), microphones),
StreamSwitcher(&mContextInstance, sinkMetadata),
StreamInHwGainHelper(&mContextInstance) {}
bool StreamInPrimary::useStubStream(const AudioDevice& device) {
static const bool kSimulateInput =
GetBoolProperty("ro.boot.audio.tinyalsa.simulate_input", false);
return kSimulateInput || device.type.type == AudioDeviceType::IN_TELEPHONY_RX ||
device.type.type == AudioDeviceType::IN_FM_TUNER ||
device.type.connection == AudioDeviceDescription::CONNECTION_BUS;
}
StreamSwitcher::DeviceSwitchBehavior StreamInPrimary::switchCurrentStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
LOG(DEBUG) << __func__;
if (devices.size() > 1) {
LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
<< devices.size();
return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
}
if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
return DeviceSwitchBehavior::USE_CURRENT_STREAM;
}
return DeviceSwitchBehavior::CREATE_NEW_STREAM;
}
std::unique_ptr<StreamCommonInterfaceEx> StreamInPrimary::createNewStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
StreamContext* context, const Metadata& metadata) {
if (devices.empty()) {
LOG(FATAL) << __func__ << ": called with empty devices"; // see 'switchCurrentStream'
}
if (useStubStream(devices[0])) {
return std::unique_ptr<StreamCommonInterfaceEx>(
new InnerStreamWrapper<StreamStub>(context, metadata));
}
return std::unique_ptr<StreamCommonInterfaceEx>(
new InnerStreamWrapper<StreamPrimary>(context, metadata));
}
ndk::ScopedAStatus StreamInPrimary::getHwGain(std::vector<float>* _aidl_return) {
if (isStubStream()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
return getHwGainImpl(_aidl_return);
}
ndk::ScopedAStatus StreamInPrimary::setHwGain(const std::vector<float>& in_channelGains) {
if (isStubStream()) {
LOG(DEBUG) << __func__ << ": gains " << ::android::internal::ToString(in_channelGains);
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
auto currentGains = mHwGains;
RETURN_STATUS_IF_ERROR(setHwGainImpl(in_channelGains));
if (in_channelGains.size() < 1) {
LOG(FATAL) << __func__ << ": unexpected gain vector size: " << in_channelGains.size();
}
if (auto status = primary::PrimaryMixer::getInstance().setMicGain(in_channelGains[0]);
!status.isOk()) {
mHwGains = currentGains;
return status;
}
return ndk::ScopedAStatus::ok();
}
StreamOutPrimary::StreamOutPrimary(StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo)
: StreamOut(std::move(context), offloadInfo),
StreamSwitcher(&mContextInstance, sourceMetadata),
StreamOutHwVolumeHelper(&mContextInstance) {}
bool StreamOutPrimary::useStubStream(const AudioDevice& device) {
static const bool kSimulateOutput =
GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false);
return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX ||
device.type.connection == AudioDeviceDescription::CONNECTION_BUS;
}
StreamSwitcher::DeviceSwitchBehavior StreamOutPrimary::switchCurrentStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
LOG(DEBUG) << __func__;
if (devices.size() > 1) {
LOG(ERROR) << __func__ << ": primary stream can only be connected to one device, got: "
<< devices.size();
return DeviceSwitchBehavior::UNSUPPORTED_DEVICES;
}
if (devices.empty() || useStubStream(devices[0]) == isStubStream()) {
return DeviceSwitchBehavior::USE_CURRENT_STREAM;
}
return DeviceSwitchBehavior::CREATE_NEW_STREAM;
}
std::unique_ptr<StreamCommonInterfaceEx> StreamOutPrimary::createNewStream(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
StreamContext* context, const Metadata& metadata) {
if (devices.empty()) {
LOG(FATAL) << __func__ << ": called with empty devices"; // see 'switchCurrentStream'
}
if (useStubStream(devices[0])) {
return std::unique_ptr<StreamCommonInterfaceEx>(
new InnerStreamWrapper<StreamStub>(context, metadata));
}
return std::unique_ptr<StreamCommonInterfaceEx>(
new InnerStreamWrapper<StreamPrimary>(context, metadata));
}
ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector<float>* _aidl_return) {
if (isStubStream()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
return getHwVolumeImpl(_aidl_return);
}
ndk::ScopedAStatus StreamOutPrimary::setHwVolume(const std::vector<float>& in_channelVolumes) {
if (isStubStream()) {
LOG(DEBUG) << __func__ << ": volumes " << ::android::internal::ToString(in_channelVolumes);
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
auto currentVolumes = mHwVolumes;
RETURN_STATUS_IF_ERROR(setHwVolumeImpl(in_channelVolumes));
if (auto status = primary::PrimaryMixer::getInstance().setVolumes(in_channelVolumes);
!status.isOk()) {
mHwVolumes = currentVolumes;
return status;
}
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::audio::core

View file

@ -33,6 +33,7 @@ namespace aidl::android::hardware::audio::core {
StreamStub::StreamStub(StreamContext* context, const Metadata& metadata)
: StreamCommonImpl(context, metadata),
mBufferSizeFrames(getContext().getBufferSizeInFrames()),
mFrameSizeBytes(getContext().getFrameSize()),
mSampleRate(getContext().getSampleRate()),
mIsAsynchronous(!!getContext().getAsyncCallback()),
@ -40,7 +41,6 @@ StreamStub::StreamStub(StreamContext* context, const Metadata& metadata)
::android::status_t StreamStub::init() {
mIsInitialized = true;
usleep(500);
return ::android::OK;
}
@ -48,7 +48,16 @@ StreamStub::StreamStub(StreamContext* context, const Metadata& metadata)
if (!mIsInitialized) {
LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
}
usleep(500);
if (!mIsInput) {
if (!mIsAsynchronous) {
static constexpr float kMicrosPerSecond = MICROS_PER_SECOND;
const size_t delayUs = static_cast<size_t>(
std::roundf(mBufferSizeFrames * kMicrosPerSecond / mSampleRate));
usleep(delayUs);
} else {
usleep(500);
}
}
return ::android::OK;
}
@ -56,7 +65,6 @@ StreamStub::StreamStub(StreamContext* context, const Metadata& metadata)
if (!mIsInitialized) {
LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
}
usleep(500);
return ::android::OK;
}
@ -64,7 +72,6 @@ StreamStub::StreamStub(StreamContext* context, const Metadata& metadata)
if (!mIsInitialized) {
LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
}
usleep(500);
return ::android::OK;
}

View file

@ -18,14 +18,11 @@
#define LOG_TAG "AHAL_StreamUsb"
#include <android-base/logging.h>
#include <Utils.h>
#include <error/expected_utils.h>
#include "UsbAlsaMixerControl.h"
#include "core-impl/StreamUsb.h"
using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioDevice;
@ -97,14 +94,15 @@ StreamOutUsb::StreamOutUsb(StreamContext&& context, const SourceMetadata& source
const std::optional<AudioOffloadInfo>& offloadInfo)
: StreamOut(std::move(context), offloadInfo),
StreamUsb(&mContextInstance, sourceMetadata),
mChannelCount(getChannelCount(getContext().getChannelLayout())) {}
StreamOutHwVolumeHelper(&mContextInstance) {}
ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
*_aidl_return = mHwVolumes;
return ndk::ScopedAStatus::ok();
return getHwVolumeImpl(_aidl_return);
}
ndk::ScopedAStatus StreamOutUsb::setHwVolume(const std::vector<float>& in_channelVolumes) {
auto currentVolumes = mHwVolumes;
RETURN_STATUS_IF_ERROR(setHwVolumeImpl(in_channelVolumes));
// Avoid using mConnectedDeviceProfiles because it requires a lock.
for (const auto& device : getConnectedDevices()) {
if (auto deviceProfile = alsa::getDeviceProfile(device, mIsInput);
@ -114,11 +112,11 @@ ndk::ScopedAStatus StreamOutUsb::setHwVolume(const std::vector<float>& in_channe
!result.isOk()) {
LOG(ERROR) << __func__
<< ": failed to set volume for device address=" << *deviceProfile;
mHwVolumes = currentVolumes;
return result;
}
}
}
mHwVolumes = in_channelVolumes;
return ndk::ScopedAStatus::ok();
}

View file

@ -55,6 +55,7 @@ cc_test {
"VtsHalAudioCoreConfigTargetTest.cpp",
"VtsHalAudioCoreModuleTargetTest.cpp",
],
test_config: "VtsHalAudioCoreTargetTest.xml",
}
cc_test {

View file

@ -3189,10 +3189,17 @@ class StreamLogicDefaultDriver : public StreamLogicDriver {
std::string mUnexpectedTransition;
};
enum { NAMED_CMD_NAME, NAMED_CMD_DELAY_MS, NAMED_CMD_STREAM_TYPE, NAMED_CMD_CMDS };
enum {
NAMED_CMD_NAME,
NAMED_CMD_DELAY_MS,
NAMED_CMD_STREAM_TYPE,
NAMED_CMD_CMDS,
NAMED_CMD_VALIDATE_POS_INCREASE
};
enum class StreamTypeFilter { ANY, SYNC, ASYNC };
using NamedCommandSequence =
std::tuple<std::string, int, StreamTypeFilter, std::shared_ptr<StateSequence>>;
std::tuple<std::string, int /*cmdDelayMs*/, StreamTypeFilter,
std::shared_ptr<StateSequence>, bool /*validatePositionIncrease*/>;
enum { PARAM_MODULE_NAME, PARAM_CMD_SEQ, PARAM_SETUP_SEQ };
using StreamIoTestParameters =
std::tuple<std::string /*moduleName*/, NamedCommandSequence, bool /*useSetupSequence2*/>;
@ -3236,10 +3243,14 @@ class AudioStreamIo : public AudioCoreModuleBase,
ASSERT_NO_FATAL_FAILURE(delayTransientStates.SetUp(module.get()));
const auto& commandsAndStates =
std::get<NAMED_CMD_CMDS>(std::get<PARAM_CMD_SEQ>(GetParam()));
const bool validatePositionIncrease =
std::get<NAMED_CMD_VALIDATE_POS_INCREASE>(std::get<PARAM_CMD_SEQ>(GetParam()));
if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates));
ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates,
validatePositionIncrease));
} else {
ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates));
ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates,
validatePositionIncrease));
}
if (isNonBlocking) {
// Also try running the same sequence with "aosp.forceTransientBurst" set.
@ -3250,11 +3261,11 @@ class AudioStreamIo : public AudioCoreModuleBase,
if (forceTransientBurst.SetUpNoChecks(module.get(), true /*failureExpected*/)
.isOk()) {
if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
ASSERT_NO_FATAL_FAILURE(
RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates));
ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(
portConfig, commandsAndStates, validatePositionIncrease));
} else {
ASSERT_NO_FATAL_FAILURE(
RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates));
ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(
portConfig, commandsAndStates, validatePositionIncrease));
}
}
} else if (!IOTraits<Stream>::is_input) {
@ -3267,11 +3278,11 @@ class AudioStreamIo : public AudioCoreModuleBase,
if (forceSynchronousDrain.SetUpNoChecks(module.get(), true /*failureExpected*/)
.isOk()) {
if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
ASSERT_NO_FATAL_FAILURE(
RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates));
ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(
portConfig, commandsAndStates, validatePositionIncrease));
} else {
ASSERT_NO_FATAL_FAILURE(
RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates));
ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(
portConfig, commandsAndStates, validatePositionIncrease));
}
}
}
@ -3285,11 +3296,13 @@ class AudioStreamIo : public AudioCoreModuleBase,
// Set up a patch first, then open a stream.
void RunStreamIoCommandsImplSeq1(const AudioPortConfig& portConfig,
std::shared_ptr<StateSequence> commandsAndStates) {
std::shared_ptr<StateSequence> commandsAndStates,
bool validatePositionIncrease) {
auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
IOTraits<Stream>::is_input, portConfig);
ASSERT_FALSE(devicePorts.empty());
auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
SCOPED_TRACE(devicePortConfig.toString());
WithAudioPatch patch(IOTraits<Stream>::is_input, portConfig, devicePortConfig);
ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
@ -3307,14 +3320,17 @@ class AudioStreamIo : public AudioCoreModuleBase,
EXPECT_FALSE(worker.hasError()) << worker.getError();
EXPECT_EQ("", driver.getUnexpectedStateTransition());
if (ValidateObservablePosition(devicePortConfig)) {
EXPECT_TRUE(driver.hasObservablePositionIncrease());
if (validatePositionIncrease) {
EXPECT_TRUE(driver.hasObservablePositionIncrease());
}
EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
}
}
// Open a stream, then set up a patch for it.
void RunStreamIoCommandsImplSeq2(const AudioPortConfig& portConfig,
std::shared_ptr<StateSequence> commandsAndStates) {
std::shared_ptr<StateSequence> commandsAndStates,
bool validatePositionIncrease) {
WithStream<Stream> stream(portConfig);
ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
StreamLogicDefaultDriver driver(commandsAndStates,
@ -3326,6 +3342,7 @@ class AudioStreamIo : public AudioCoreModuleBase,
IOTraits<Stream>::is_input, portConfig);
ASSERT_FALSE(devicePorts.empty());
auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
SCOPED_TRACE(devicePortConfig.toString());
WithAudioPatch patch(IOTraits<Stream>::is_input, stream.getPortConfig(), devicePortConfig);
ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
@ -3336,7 +3353,9 @@ class AudioStreamIo : public AudioCoreModuleBase,
EXPECT_FALSE(worker.hasError()) << worker.getError();
EXPECT_EQ("", driver.getUnexpectedStateTransition());
if (ValidateObservablePosition(devicePortConfig)) {
EXPECT_TRUE(driver.hasObservablePositionIncrease());
if (validatePositionIncrease) {
EXPECT_TRUE(driver.hasObservablePositionIncrease());
}
EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
}
}
@ -3668,22 +3687,28 @@ std::shared_ptr<StateSequence> makeBurstCommands(bool isSync) {
using State = StreamDescriptor::State;
auto d = std::make_unique<StateDag>();
StateDag::Node last = d->makeFinalNode(State::ACTIVE);
StateDag::Node active = d->makeNode(State::ACTIVE, kBurstCommand, last);
// Use a couple of bursts to ensure that the driver starts reporting the position.
StateDag::Node active2 = d->makeNode(State::ACTIVE, kBurstCommand, last);
StateDag::Node active = d->makeNode(State::ACTIVE, kBurstCommand, active2);
StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
if (!isSync) {
// Allow optional routing via the TRANSFERRING state on bursts.
active.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, last));
active2.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, last));
active.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, active2));
idle.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, active));
}
d->makeNode(State::STANDBY, kStartCommand, idle);
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kReadSeq =
std::make_tuple(std::string("Read"), 0, StreamTypeFilter::ANY, makeBurstCommands(true));
std::make_tuple(std::string("Read"), 0, StreamTypeFilter::ANY, makeBurstCommands(true),
true /*validatePositionIncrease*/);
static const NamedCommandSequence kWriteSyncSeq =
std::make_tuple(std::string("Write"), 0, StreamTypeFilter::SYNC, makeBurstCommands(true));
std::make_tuple(std::string("Write"), 0, StreamTypeFilter::SYNC, makeBurstCommands(true),
true /*validatePositionIncrease*/);
static const NamedCommandSequence kWriteAsyncSeq =
std::make_tuple(std::string("Write"), 0, StreamTypeFilter::ASYNC, makeBurstCommands(false));
std::make_tuple(std::string("Write"), 0, StreamTypeFilter::ASYNC, makeBurstCommands(false),
true /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makeAsyncDrainCommands(bool isInput) {
using State = StreamDescriptor::State;
@ -3711,11 +3736,12 @@ std::shared_ptr<StateSequence> makeAsyncDrainCommands(bool isInput) {
}
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kWriteDrainAsyncSeq =
std::make_tuple(std::string("WriteDrain"), kStreamTransientStateTransitionDelayMs,
StreamTypeFilter::ASYNC, makeAsyncDrainCommands(false));
static const NamedCommandSequence kDrainInSeq = std::make_tuple(
std::string("Drain"), 0, StreamTypeFilter::ANY, makeAsyncDrainCommands(true));
static const NamedCommandSequence kWriteDrainAsyncSeq = std::make_tuple(
std::string("WriteDrain"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
makeAsyncDrainCommands(false), false /*validatePositionIncrease*/);
static const NamedCommandSequence kDrainInSeq =
std::make_tuple(std::string("Drain"), 0, StreamTypeFilter::ANY,
makeAsyncDrainCommands(true), false /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makeDrainOutCommands(bool isSync) {
using State = StreamDescriptor::State;
@ -3735,10 +3761,12 @@ std::shared_ptr<StateSequence> makeDrainOutCommands(bool isSync) {
d->makeNode(State::STANDBY, kStartCommand, idle);
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kDrainOutSyncSeq = std::make_tuple(
std::string("Drain"), 0, StreamTypeFilter::SYNC, makeDrainOutCommands(true));
static const NamedCommandSequence kDrainOutAsyncSeq = std::make_tuple(
std::string("Drain"), 0, StreamTypeFilter::ASYNC, makeDrainOutCommands(false));
static const NamedCommandSequence kDrainOutSyncSeq =
std::make_tuple(std::string("Drain"), 0, StreamTypeFilter::SYNC, makeDrainOutCommands(true),
false /*validatePositionIncrease*/);
static const NamedCommandSequence kDrainOutAsyncSeq =
std::make_tuple(std::string("Drain"), 0, StreamTypeFilter::ASYNC,
makeDrainOutCommands(false), false /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makeDrainPauseOutCommands(bool isSync) {
using State = StreamDescriptor::State;
@ -3759,12 +3787,12 @@ std::shared_ptr<StateSequence> makeDrainPauseOutCommands(bool isSync) {
d->makeNode(State::STANDBY, kStartCommand, idle);
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kDrainPauseOutSyncSeq =
std::make_tuple(std::string("DrainPause"), kStreamTransientStateTransitionDelayMs,
StreamTypeFilter::SYNC, makeDrainPauseOutCommands(true));
static const NamedCommandSequence kDrainPauseOutAsyncSeq =
std::make_tuple(std::string("DrainPause"), kStreamTransientStateTransitionDelayMs,
StreamTypeFilter::ASYNC, makeDrainPauseOutCommands(false));
static const NamedCommandSequence kDrainPauseOutSyncSeq = std::make_tuple(
std::string("DrainPause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::SYNC,
makeDrainPauseOutCommands(true), false /*validatePositionIncrease*/);
static const NamedCommandSequence kDrainPauseOutAsyncSeq = std::make_tuple(
std::string("DrainPause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
makeDrainPauseOutCommands(false), false /*validatePositionIncrease*/);
// This sequence also verifies that the capture / presentation position is not reset on standby.
std::shared_ptr<StateSequence> makeStandbyCommands(bool isInput, bool isSync) {
@ -3805,13 +3833,15 @@ std::shared_ptr<StateSequence> makeStandbyCommands(bool isInput, bool isSync) {
}
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kStandbyInSeq = std::make_tuple(
std::string("Standby"), 0, StreamTypeFilter::ANY, makeStandbyCommands(true, false));
static const NamedCommandSequence kStandbyOutSyncSeq = std::make_tuple(
std::string("Standby"), 0, StreamTypeFilter::SYNC, makeStandbyCommands(false, true));
static const NamedCommandSequence kStandbyOutAsyncSeq =
std::make_tuple(std::string("Standby"), kStreamTransientStateTransitionDelayMs,
StreamTypeFilter::ASYNC, makeStandbyCommands(false, false));
static const NamedCommandSequence kStandbyInSeq =
std::make_tuple(std::string("Standby"), 0, StreamTypeFilter::ANY,
makeStandbyCommands(true, false), false /*validatePositionIncrease*/);
static const NamedCommandSequence kStandbyOutSyncSeq =
std::make_tuple(std::string("Standby"), 0, StreamTypeFilter::SYNC,
makeStandbyCommands(false, true), false /*validatePositionIncrease*/);
static const NamedCommandSequence kStandbyOutAsyncSeq = std::make_tuple(
std::string("Standby"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
makeStandbyCommands(false, false), false /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makePauseCommands(bool isInput, bool isSync) {
using State = StreamDescriptor::State;
@ -3846,13 +3876,15 @@ std::shared_ptr<StateSequence> makePauseCommands(bool isInput, bool isSync) {
}
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kPauseInSeq = std::make_tuple(
std::string("Pause"), 0, StreamTypeFilter::ANY, makePauseCommands(true, false));
static const NamedCommandSequence kPauseOutSyncSeq = std::make_tuple(
std::string("Pause"), 0, StreamTypeFilter::SYNC, makePauseCommands(false, true));
static const NamedCommandSequence kPauseOutAsyncSeq =
std::make_tuple(std::string("Pause"), kStreamTransientStateTransitionDelayMs,
StreamTypeFilter::ASYNC, makePauseCommands(false, false));
static const NamedCommandSequence kPauseInSeq =
std::make_tuple(std::string("Pause"), 0, StreamTypeFilter::ANY,
makePauseCommands(true, false), false /*validatePositionIncrease*/);
static const NamedCommandSequence kPauseOutSyncSeq =
std::make_tuple(std::string("Pause"), 0, StreamTypeFilter::SYNC,
makePauseCommands(false, true), false /*validatePositionIncrease*/);
static const NamedCommandSequence kPauseOutAsyncSeq = std::make_tuple(
std::string("Pause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
makePauseCommands(false, false), false /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makeFlushCommands(bool isInput, bool isSync) {
using State = StreamDescriptor::State;
@ -3879,13 +3911,15 @@ std::shared_ptr<StateSequence> makeFlushCommands(bool isInput, bool isSync) {
}
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kFlushInSeq = std::make_tuple(
std::string("Flush"), 0, StreamTypeFilter::ANY, makeFlushCommands(true, false));
static const NamedCommandSequence kFlushOutSyncSeq = std::make_tuple(
std::string("Flush"), 0, StreamTypeFilter::SYNC, makeFlushCommands(false, true));
static const NamedCommandSequence kFlushOutAsyncSeq =
std::make_tuple(std::string("Flush"), kStreamTransientStateTransitionDelayMs,
StreamTypeFilter::ASYNC, makeFlushCommands(false, false));
static const NamedCommandSequence kFlushInSeq =
std::make_tuple(std::string("Flush"), 0, StreamTypeFilter::ANY,
makeFlushCommands(true, false), false /*validatePositionIncrease*/);
static const NamedCommandSequence kFlushOutSyncSeq =
std::make_tuple(std::string("Flush"), 0, StreamTypeFilter::SYNC,
makeFlushCommands(false, true), false /*validatePositionIncrease*/);
static const NamedCommandSequence kFlushOutAsyncSeq = std::make_tuple(
std::string("Flush"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
makeFlushCommands(false, false), false /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makeDrainPauseFlushOutCommands(bool isSync) {
using State = StreamDescriptor::State;
@ -3906,10 +3940,12 @@ std::shared_ptr<StateSequence> makeDrainPauseFlushOutCommands(bool isSync) {
}
static const NamedCommandSequence kDrainPauseFlushOutSyncSeq =
std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
StreamTypeFilter::SYNC, makeDrainPauseFlushOutCommands(true));
StreamTypeFilter::SYNC, makeDrainPauseFlushOutCommands(true),
false /*validatePositionIncrease*/);
static const NamedCommandSequence kDrainPauseFlushOutAsyncSeq =
std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
StreamTypeFilter::ASYNC, makeDrainPauseFlushOutCommands(false));
StreamTypeFilter::ASYNC, makeDrainPauseFlushOutCommands(false),
false /*validatePositionIncrease*/);
// Note, this isn't the "official" enum printer, it is only used to make the test name suffix.
std::string PrintStreamFilterToString(StreamTypeFilter filter) {

View file

@ -25,6 +25,11 @@
<option name="teardown-command" value="setprop vts.native_server.on 0"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="VtsHalAudioCoreTargetTest->/data/local/tmp/VtsHalAudioCoreTargetTest" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsHalAudioCoreTargetTest" />